weekly release 2.5dev
[moodle.git] / course / externallib.php
CommitLineData
8e7d3fe4 1<?php
8e7d3fe4
PS
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.
13//
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/>.
16
4615817d 17
8e7d3fe4 18/**
6bb31e40 19 * External course API
8e7d3fe4 20 *
4615817d
JM
21 * @package core_course
22 * @category external
23 * @copyright 2009 Petr Skodak
8e7d3fe4
PS
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26
df997f84
PS
27defined('MOODLE_INTERNAL') || die;
28
8e7d3fe4
PS
29require_once("$CFG->libdir/externallib.php");
30
5d1017e1 31/**
4615817d
JM
32 * Course external functions
33 *
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
5d1017e1
JM
39 */
40class core_course_external extends external_api {
8e7d3fe4 41
ec0d6ea2
DC
42 /**
43 * Returns description of method parameters
4615817d 44 *
ec0d6ea2 45 * @return external_function_parameters
4615817d 46 * @since Moodle 2.2
ec0d6ea2
DC
47 */
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.')
55 )
56 ), 'Options, not used yet, might be used in later version', VALUE_DEFAULT, array())
57 )
58 );
59 }
60
61 /**
62 * Get course contents
4615817d
JM
63 *
64 * @param int $courseid course id
65 * @param array $options These options are not used yet, might be used in later version
ec0d6ea2 66 * @return array
4615817d 67 * @since Moodle 2.2
ec0d6ea2 68 */
3297d575 69 public static function get_course_contents($courseid, $options = array()) {
ec0d6ea2
DC
70 global $CFG, $DB;
71 require_once($CFG->dirroot . "/course/lib.php");
72
73 //validate parameter
74 $params = self::validate_parameters(self::get_course_contents_parameters(),
75 array('courseid' => $courseid, 'options' => $options));
76
77 //retrieve the course
78 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
79
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');
85 }
86
87 // now security checks
1f364c87 88 $context = context_course::instance($course->id, IGNORE_MISSING);
ec0d6ea2
DC
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);
96 }
97
98 $canupdatecourse = has_capability('moodle/course:update', $context);
99
100 //create return value
101 $coursecontents = array();
102
103 if ($canupdatecourse or $course->visible
104 or has_capability('moodle/course:viewhiddencourses', $context)) {
105
106 //retrieve sections
107 $modinfo = get_fast_modinfo($course);
71a56e08 108 $sections = $modinfo->get_section_info_all();
ec0d6ea2
DC
109
110 //for each sections (first displayed to last displayed)
111 foreach ($sections as $key => $section) {
112
71a56e08 113 if (!$section->uservisible) {
ec0d6ea2
DC
114 continue;
115 }
116
117 // reset $sectioncontents
118 $sectionvalues = array();
119 $sectionvalues['id'] = $section->id;
120 $sectionvalues['name'] = get_section_name($course, $section);
ec0d6ea2 121 $sectionvalues['visible'] = $section->visible;
93ce0e82
JM
122 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
123 external_format_text($section->summary, $section->summaryformat,
124 $context->id, 'course', 'section', $section->id);
ec0d6ea2
DC
125 $sectioncontents = array();
126
127 //for each module of the section
9a36be73 128 foreach ($modinfo->sections[$section->section] as $cmid) {
ec0d6ea2
DC
129 $cm = $modinfo->cms[$cmid];
130
131 // stop here if the module is not visible to the user
132 if (!$cm->uservisible) {
133 continue;
134 }
135
136 $module = array();
137
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;
145
9a5e297b 146 $modcontext = context_module::instance($cm->id);
ec0d6ea2
DC
147
148 if (!empty($cm->showdescription)) {
149 $module['description'] = $cm->get_content();
150 }
151
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();
156 }
157
158 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
9a5e297b 159 context_module::instance($cm->id));
ec0d6ea2
DC
160 //user that can view hidden module should know about the visibility
161 $module['visible'] = $cm->visible;
162
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;
167 }
168
169 $baseurl = 'webservice/pluginfile.php';
170
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;
178 }
179 }
180
181 //assign result to $sectioncontents
182 $sectioncontents[] = $module;
183
184 }
185 $sectionvalues['modules'] = $sectioncontents;
186
187 // assign result to $coursecontents
188 $coursecontents[] = $sectionvalues;
189 }
190 }
191 return $coursecontents;
192 }
193
194 /**
195 * Returns description of method result value
4615817d 196 *
ec0d6ea2 197 * @return external_description
4615817d 198 * @since Moodle 2.2
ec0d6ea2
DC
199 */
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'),
93ce0e82 208 'summaryformat' => new external_format_value('summary'),
ec0d6ea2
DC
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),
11d81936 214 'name' => new external_value(PARAM_RAW, 'activity module name'),
ec0d6ea2
DC
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'),
236
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'),
241 )
242 ), VALUE_DEFAULT, array()
243 )
244 )
245 ), 'list of module'
246 )
247 )
248 )
249 );
250 }
251
6bb31e40 252 /**
253 * Returns description of method parameters
4615817d 254 *
6bb31e40 255 * @return external_function_parameters
754c2dea 256 * @since Moodle 2.3
6bb31e40 257 */
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())
267 )
268 );
269 }
270
271 /**
272 * Get courses
4615817d
JM
273 *
274 * @param array $options It contains an array (list of ids)
6bb31e40 275 * @return array
4615817d 276 * @since Moodle 2.2
6bb31e40 277 */
3297d575 278 public static function get_courses($options = array()) {
6bb31e40 279 global $CFG, $DB;
280 require_once($CFG->dirroot . "/course/lib.php");
281
282 //validate parameter
283 $params = self::validate_parameters(self::get_courses_parameters(),
284 array('options' => $options));
285
286 //retrieve courses
12fc8acf 287 if (!array_key_exists('ids', $params['options'])
6bb31e40 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']);
292 }
293
294 //create return value
295 $coursesinfo = array();
296 foreach ($courses as $course) {
297
298 // now security checks
1f364c87 299 $context = context_course::instance($course->id, IGNORE_MISSING);
0e984d98 300 $courseformatoptions = course_get_format($course)->get_format_options();
6bb31e40 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;
96d3b93b 307 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
6bb31e40 308 }
309 require_capability('moodle/course:view', $context);
310
311 $courseinfo = array();
312 $courseinfo['id'] = $course->id;
313 $courseinfo['fullname'] = $course->fullname;
314 $courseinfo['shortname'] = $course->shortname;
315 $courseinfo['categoryid'] = $course->category;
93ce0e82
JM
316 list($courseinfo['summary'], $courseinfo['summaryformat']) =
317 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
6bb31e40 318 $courseinfo['format'] = $course->format;
319 $courseinfo['startdate'] = $course->startdate;
0e984d98
MG
320 if (array_key_exists('numsections', $courseformatoptions)) {
321 // For backward-compartibility
322 $courseinfo['numsections'] = $courseformatoptions['numsections'];
323 }
6bb31e40 324
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;
0e984d98
MG
335 if (array_key_exists('hiddensections', $courseformatoptions)) {
336 // For backward-compartibility
337 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
338 }
6bb31e40 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;
8d8d4da4 349 $courseinfo['courseformatoptions'] = array();
0e984d98 350 foreach ($courseformatoptions as $key => $value) {
8d8d4da4
MG
351 $courseinfo['courseformatoptions'][] = array(
352 'name' => $key,
353 'value' => $value
0e984d98
MG
354 );
355 }
6bb31e40 356 }
357
358 if ($courseadmin or $course->visible
359 or has_capability('moodle/course:viewhiddencourses', $context)) {
360 $coursesinfo[] = $courseinfo;
361 }
362 }
363
364 return $coursesinfo;
365 }
366
367 /**
368 * Returns description of method result value
4615817d 369 *
6bb31e40 370 * @return external_description
4615817d 371 * @since Moodle 2.2
6bb31e40 372 */
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'),
93ce0e82 385 'summaryformat' => new external_format_value('summary'),
aff24313 386 'format' => new external_value(PARAM_PLUGIN,
6bb31e40 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'),
0e984d98 394 'numsections' => new external_value(PARAM_INT,
8d8d4da4 395 '(deprecated, use courseformatoptions) number of weeks/topics',
0e984d98 396 VALUE_OPTIONAL),
6bb31e40 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,
8d8d4da4 405 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
3ec163dd
EL
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),
8d8d4da4 431 'courseformatoptions' => new external_multiple_structure(
0e984d98 432 new external_single_structure(
8d8d4da4
MG
433 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
434 'value' => new external_value(PARAM_RAW, 'course format option value')
0e984d98
MG
435 )),
436 'additional options for particular course format', VALUE_OPTIONAL
437 ),
3ec163dd 438 ), 'course'
479a5db1 439 )
479a5db1
FS
440 );
441 }
442
6bb31e40 443 /**
444 * Returns description of method parameters
4615817d 445 *
6bb31e40 446 * @return external_function_parameters
4615817d 447 * @since Moodle 2.2
6bb31e40 448 */
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),
93ce0e82 461 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
aff24313 462 'format' => new external_value(PARAM_PLUGIN,
6bb31e40 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),
0e984d98 473 'numsections' => new external_value(PARAM_INT,
8d8d4da4 474 '(deprecated, use courseformatoptions) number of weeks/topics',
0e984d98 475 VALUE_OPTIONAL),
6bb31e40 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,
8d8d4da4 485 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
0e984d98 486 VALUE_OPTIONAL),
6bb31e40 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,
8a6b1193 494 'Enabled, control via completion and activity settings. Disabled,
6bb31e40 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),
aff24313 503 'lang' => new external_value(PARAM_SAFEDIR,
6bb31e40 504 'forced course language', VALUE_OPTIONAL),
aff24313 505 'forcetheme' => new external_value(PARAM_PLUGIN,
6bb31e40 506 'name of the force theme', VALUE_OPTIONAL),
8d8d4da4 507 'courseformatoptions' => new external_multiple_structure(
0e984d98 508 new external_single_structure(
8d8d4da4
MG
509 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
510 'value' => new external_value(PARAM_RAW, 'course format option value')
0e984d98
MG
511 )),
512 'additional options for particular course format', VALUE_OPTIONAL),
6bb31e40 513 )
514 ), 'courses to create'
515 )
516 )
517 );
518 }
519
520 /**
521 * Create courses
4615817d 522 *
6bb31e40 523 * @param array $courses
524 * @return array courses (id and shortname only)
4615817d 525 * @since Moodle 2.2
6bb31e40 526 */
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');
531
6bb31e40 532 $params = self::validate_parameters(self::create_courses_parameters(),
533 array('courses' => $courses));
534
535 $availablethemes = get_plugin_list('theme');
536 $availablelangs = get_string_manager()->get_list_of_translations();
537
538 $transaction = $DB->start_delegated_transaction();
539
540 foreach ($params['courses'] as $course) {
541
542 // Ensure the current user is allowed to run this function
1f364c87 543 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
6bb31e40 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'];
96d3b93b 550 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
6bb31e40 551 }
552 require_capability('moodle/course:create', $context);
553
554 // Make sure lang is valid
12fc8acf 555 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
96d3b93b 556 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
6bb31e40 557 }
558
559 // Make sure theme is valid
12fc8acf 560 if (array_key_exists('forcetheme', $course)) {
6bb31e40 561 if (!empty($CFG->allowcoursethemes)) {
562 if (empty($availablethemes[$course['forcetheme']])) {
96d3b93b 563 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
6bb31e40 564 } else {
565 $course['theme'] = $course['forcetheme'];
566 }
567 }
568 }
569
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;
574 }
575
576 //set default value for completion
8a6b1193 577 $courseconfig = get_config('moodlecourse');
6bb31e40 578 if (completion_info::is_enabled_for_site()) {
12fc8acf 579 if (!array_key_exists('enablecompletion', $course)) {
8a6b1193 580 $course['enablecompletion'] = $courseconfig->enablecompletion;
6bb31e40 581 }
12fc8acf 582 if (!array_key_exists('completionstartonenrol', $course)) {
8a6b1193 583 $course['completionstartonenrol'] = $courseconfig->completionstartonenrol;
6bb31e40 584 }
585 } else {
586 $course['enablecompletion'] = 0;
587 $course['completionstartonenrol'] = 0;
588 }
589
590 $course['category'] = $course['categoryid'];
591
93ce0e82
JM
592 // Summary format.
593 $course['summaryformat'] = external_validate_format($course['summaryformat']);
594
8d8d4da4
MG
595 if (!empty($course['courseformatoptions'])) {
596 foreach ($course['courseformatoptions'] as $option) {
597 $course[$option['name']] = $option['value'];
0e984d98
MG
598 }
599 }
600
6bb31e40 601 //Note: create_course() core function check shortname, idnumber, category
602 $course['id'] = create_course((object) $course)->id;
603
604 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
605 }
606
607 $transaction->allow_commit();
608
609 return $resultcourses;
610 }
611
612 /**
613 * Returns description of method result value
4615817d 614 *
6bb31e40 615 * @return external_description
4615817d 616 * @since Moodle 2.2
6bb31e40 617 */
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'),
624 )
625 )
626 );
627 }
628
63a85dc7
JL
629 /**
630 * Returns description of method parameters
3ec163dd 631 *
63a85dc7 632 * @return external_function_parameters
3ec163dd 633 * @since Moodle 2.2
63a85dc7
JL
634 */
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')),
639 )
640 );
641 }
642
643 /**
644 * Delete courses
3ec163dd 645 *
63a85dc7 646 * @param array $courseids A list of course ids
3ec163dd 647 * @since Moodle 2.2
63a85dc7
JL
648 */
649 public static function delete_courses($courseids) {
650 global $CFG, $DB;
651 require_once($CFG->dirroot."/course/lib.php");
652
653 // Parameter validation.
654 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
655
656 $transaction = $DB->start_delegated_transaction();
657
658 foreach ($params['courseids'] as $courseid) {
659 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
660
661 // Check if the context is valid.
662 $coursecontext = context_course::instance($course->id);
663 self::validate_context($coursecontext);
664
665 // Check if the current user has enought permissions.
666 if (!can_delete_course($courseid)) {
d6ebe011
JM
667 throw new moodle_exception('cannotdeletecategorycourse', 'error',
668 '', format_string($course->fullname)." (id: $courseid)");
63a85dc7
JL
669 }
670
671 delete_course($course, false);
672 }
673
674 $transaction->allow_commit();
675
676 return null;
677 }
678
679 /**
680 * Returns description of method result value
3ec163dd 681 *
63a85dc7 682 * @return external_description
3ec163dd 683 * @since Moodle 2.2
63a85dc7
JL
684 */
685 public static function delete_courses_returns() {
686 return null;
687 }
688
3dc1d76e
JL
689 /**
690 * Returns description of method parameters
691 *
692 * @return external_function_parameters
693 * @since Moodle 2.3
694 */
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(
3dfc29e1 706 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
9aa84e91
JL
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),
9aa84e91
JL
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)'
716 ),
717 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
718 )
3dc1d76e
JL
719 )
720 ), VALUE_DEFAULT, array()
721 ),
722 )
723 );
724 }
725
726 /**
727 * Duplicate a course
728 *
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
737 */
3297d575 738 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
3dc1d76e
JL
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');
742
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
753 )
754 );
755
3ec163dd
EL
756 // Context validation.
757
758 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
19a86468 759 throw new moodle_exception('invalidcourseid', 'error');
3ec163dd
EL
760 }
761
762 // Category where duplicated course is going to be created.
763 $categorycontext = context_coursecat::instance($params['categoryid']);
764 self::validate_context($categorycontext);
765
766 // Course to be duplicated.
767 $coursecontext = context_course::instance($course->id);
768 self::validate_context($coursecontext);
769
770 $backupdefaults = array(
771 'activities' => 1,
772 'blocks' => 1,
773 'filters' => 1,
774 'users' => 0,
775 'role_assignments' => 0,
3ec163dd
EL
776 'comments' => 0,
777 'completion_information' => 0,
778 'logs' => 0,
779 'histories' => 0
780 );
781
782 $backupsettings = array();
783 // Check for backup and restore options.
784 if (!empty($params['options'])) {
785 foreach ($params['options'] as $option) {
786
787 // Strict check for a correct value (allways 1 or 0, true or false).
788 $value = clean_param($option['value'], PARAM_INT);
789
790 if ($value !== 0 and $value !== 1) {
791 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
792 }
793
794 if (!isset($backupdefaults[$option['name']])) {
795 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
796 }
797
798 $backupsettings[$option['name']] = $value;
799 }
800 }
801
802 // Capability checking.
803
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);
808
809 if (!empty($backupsettings['users'])) {
810 require_capability('moodle/backup:userinfo', $coursecontext);
811 require_capability('moodle/restore:userinfo', $categorycontext);
812 }
813
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;
818 }
819
820 $foundcoursenamestring = implode(',', $foundcoursenames);
821 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
822 }
823
824 // Backup the course.
825
826 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
827 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
828
829 foreach ($backupsettings as $name => $value) {
830 $bc->get_plan()->get_setting($name)->set_value($value);
831 }
832
833 $backupid = $bc->get_backupid();
834 $backupbasepath = $bc->get_plan()->get_basepath();
835
836 $bc->execute_plan();
837 $results = $bc->get_results();
838 $file = $results['backup_destination'];
839
840 $bc->destroy();
841
842 // Restore the backup immediately.
843
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);
847 }
848
849 // Create new course.
850 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
851
852 $rc = new restore_controller($backupid, $newcourseid,
853 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
854
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);
859 }
860 }
861
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);
867 }
868
869 $errorinfo = '';
870
871 foreach ($precheckresults['errors'] as $error) {
872 $errorinfo .= $error;
873 }
874
875 if (array_key_exists('warnings', $precheckresults)) {
876 foreach ($precheckresults['warnings'] as $warning) {
877 $errorinfo .= $warning;
878 }
879 }
880
881 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
882 }
883 }
884
885 $rc->execute_plan();
886 $rc->destroy();
887
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'];
892
893 // Set shortname and fullname back.
894 $DB->update_record('course', $course);
895
896 if (empty($CFG->keeptempdirectoriesonbackup)) {
897 fulldelete($backupbasepath);
898 }
899
900 // Delete the course backup file created by this WebService. Originally located in the course backups area.
901 $file->delete();
902
903 return array('id' => $course->id, 'shortname' => $course->shortname);
904 }
905
906 /**
907 * Returns description of method result value
908 *
909 * @return external_description
910 * @since Moodle 2.3
911 */
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'),
917 )
918 );
919 }
920
8430d87b 921 /**
c1483c9c 922 * Returns description of method parameters for import_course
8430d87b
JL
923 *
924 * @return external_function_parameters
c1483c9c 925 * @since Moodle 2.4
8430d87b
JL
926 */
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)'
940 ),
941 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
942 )
943 )
944 ), VALUE_DEFAULT, array()
945 ),
946 )
947 );
948 }
949
950 /**
951 * Imports a course
952 *
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
c1483c9c 958 * @since Moodle 2.4
8430d87b 959 */
b5bd42e8 960 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
8430d87b
JL
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');
964
965 // Parameter validation.
966 $params = self::validate_parameters(
c1483c9c
SH
967 self::import_course_parameters(),
968 array(
969 'importfrom' => $importfrom,
970 'importto' => $importto,
971 'deletecontent' => $deletecontent,
972 'options' => $options
973 )
8430d87b
JL
974 );
975
976 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
977 throw new moodle_exception('invalidextparam', 'webservice', '', $option['deletecontent']);
978 }
979
980 // Context validation.
981
982 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
0b9a3d7a 983 throw new moodle_exception('invalidcourseid', 'error');
8430d87b
JL
984 }
985
986 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
0b9a3d7a 987 throw new moodle_exception('invalidcourseid', 'error');
8430d87b
JL
988 }
989
990 $importfromcontext = context_course::instance($importfrom->id);
991 self::validate_context($importfromcontext);
992
993 $importtocontext = context_course::instance($importto->id);
994 self::validate_context($importtocontext);
995
996 $backupdefaults = array(
c1483c9c
SH
997 'activities' => 1,
998 'blocks' => 1,
999 'filters' => 1
8430d87b
JL
1000 );
1001
1002 $backupsettings = array();
1003
1004 // Check for backup and restore options.
1005 if (!empty($params['options'])) {
1006 foreach ($params['options'] as $option) {
1007
1008 // Strict check for a correct value (allways 1 or 0, true or false).
1009 $value = clean_param($option['value'], PARAM_INT);
1010
1011 if ($value !== 0 and $value !== 1) {
1012 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1013 }
1014
1015 if (!isset($backupdefaults[$option['name']])) {
1016 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1017 }
1018
1019 $backupsettings[$option['name']] = $value;
1020 }
1021 }
1022
1023 // Capability checking.
1024
1025 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1026 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1027
1028 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1029 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1030
1031 foreach ($backupsettings as $name => $value) {
1032 $bc->get_plan()->get_setting($name)->set_value($value);
1033 }
1034
1035 $backupid = $bc->get_backupid();
1036 $backupbasepath = $bc->get_plan()->get_basepath();
1037
1038 $bc->execute_plan();
1039 $bc->destroy();
1040
1041 // Restore the backup immediately.
1042
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;
1048 }
1049
1050 $rc = new restore_controller($backupid, $importto->id,
1051 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1052
1053 foreach ($backupsettings as $name => $value) {
1054 $rc->get_plan()->get_setting($name)->set_value($value);
1055 }
1056
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);
1062 }
1063
1064 $errorinfo = '';
1065
1066 foreach ($precheckresults['errors'] as $error) {
1067 $errorinfo .= $error;
1068 }
1069
1070 if (array_key_exists('warnings', $precheckresults)) {
1071 foreach ($precheckresults['warnings'] as $warning) {
1072 $errorinfo .= $warning;
1073 }
1074 }
1075
1076 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1077 }
1078 } else {
1079 if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1080 restore_dbops::delete_course_content($importto->id);
1081 }
1082 }
1083
1084 $rc->execute_plan();
1085 $rc->destroy();
1086
1087 if (empty($CFG->keeptempdirectoriesonbackup)) {
1088 fulldelete($backupbasepath);
1089 }
1090
1091 return null;
1092 }
1093
1094 /**
1095 * Returns description of method result value
1096 *
1097 * @return external_description
c1483c9c 1098 * @since Moodle 2.4
8430d87b
JL
1099 */
1100 public static function import_course_returns() {
1101 return null;
1102 }
1103
3ec163dd
EL
1104 /**
1105 * Returns description of method parameters
1106 *
1107 * @return external_function_parameters
1108 * @since Moodle 2.3
1109 */
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,'.
e6d1218a
JM
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.'.
3ec163dd 1125 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
e6d1218a 1126 '"theme" (string) only return the categories having this theme'.
3ec163dd
EL
1127 ' - user must have \'moodle/category:manage\' to search on theme'),
1128 'value' => new external_value(PARAM_RAW, 'the value to match')
1129 )
7a384506 1130 ), 'criteria', VALUE_DEFAULT, array()
3ec163dd
EL
1131 ),
1132 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1133 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1134 )
1135 );
1136 }
1137
1138 /**
1139 * Get categories
1140 *
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
1145 */
1146 public static function get_categories($criteria = array(), $addsubcategories = true) {
1147 global $CFG, $DB;
1148 require_once($CFG->dirroot . "/course/lib.php");
1149
1150 // Validate parameters.
1151 $params = self::validate_parameters(self::get_categories_parameters(),
1152 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1153
1154 // Retrieve the categories.
1155 $categories = array();
1156 if (!empty($params['criteria'])) {
1157
1158 $conditions = array();
1159 $wheres = array();
1160 foreach ($params['criteria'] as $crit) {
1161 $key = trim($crit['key']);
1162
1163 // Trying to avoid duplicate keys.
1164 if (!isset($conditions[$key])) {
3dc1d76e 1165
3ec163dd
EL
1166 $context = context_system::instance();
1167 $value = null;
1168 switch ($key) {
1169 case 'id':
1170 $value = clean_param($crit['value'], PARAM_INT);
1171 break;
3dc1d76e 1172
3ec163dd
EL
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.');
1182 }
1183 break;
3dc1d76e 1184
3ec163dd
EL
1185 case 'name':
1186 $value = clean_param($crit['value'], PARAM_TEXT);
1187 break;
3dc1d76e 1188
3ec163dd
EL
1189 case 'parent':
1190 $value = clean_param($crit['value'], PARAM_INT);
1191 break;
9aa84e91 1192
3ec163dd
EL
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.');
1202 }
1203 break;
9aa84e91 1204
3ec163dd
EL
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.');
1212 }
1213 break;
9aa84e91 1214
3ec163dd
EL
1215 default:
1216 throw new moodle_exception('criteriaerror',
1217 'webservice', '', null,
1218 'You can not search on this criteria: ' . $key);
1219 }
9aa84e91 1220
3ec163dd
EL
1221 if (isset($value)) {
1222 $conditions[$key] = $crit['value'];
1223 $wheres[] = $key . " = :" . $key;
1224 }
9aa84e91 1225 }
9aa84e91 1226 }
9aa84e91 1227
3ec163dd
EL
1228 if (!empty($wheres)) {
1229 $wheres = implode(" AND ", $wheres);
3dc1d76e 1230
3ec163dd 1231 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
3dc1d76e 1232
3ec163dd
EL
1233 // Retrieve its sub subcategories (all levels).
1234 if ($categories and !empty($params['addsubcategories'])) {
1235 $newcategories = array();
9aa84e91 1236
e6d1218a
JM
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'];
1243 }
1244 if (isset($conditions['theme'])) {
1245 $additionalselect .= ' AND theme= :theme';
1246 $additionalparams['theme'] = $conditions['theme'];
1247 }
1248
3ec163dd 1249 foreach ($categories as $category) {
e6d1218a
JM
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);
3ec163dd
EL
1253 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1254 }
1255 $categories = $categories + $newcategories;
1256 }
3dc1d76e
JL
1257 }
1258
3ec163dd
EL
1259 } else {
1260 // Retrieve all categories in the database.
1261 $categories = $DB->get_records('course_categories');
3dc1d76e
JL
1262 }
1263
3ec163dd
EL
1264 // The not returned categories. key => category id, value => reason of exclusion.
1265 $excludedcats = array();
3dc1d76e 1266
3ec163dd
EL
1267 // The returned categories.
1268 $categoriesinfo = array();
6c7d3e31 1269
3ec163dd
EL
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");
3dc1d76e 1273
3ec163dd 1274 foreach ($categories as $category) {
3dc1d76e 1275
3ec163dd
EL
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';
1284 }
1285 }
9aa84e91 1286
3ec163dd
EL
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';
1291 }
3dc1d76e 1292
3ec163dd
EL
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';
3dc1d76e 1299
3ec163dd
EL
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);
1306 }
9aa84e91 1307 }
3dc1d76e 1308
3ec163dd
EL
1309 // Return the category information.
1310 if (!isset($excludedcats[$category->id])) {
3dc1d76e 1311
3ec163dd
EL
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)) {
3dc1d76e 1316
3ec163dd
EL
1317 $categoryinfo = array();
1318 $categoryinfo['id'] = $category->id;
1319 $categoryinfo['name'] = $category->name;
93ce0e82
JM
1320 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1321 external_format_text($category->description, $category->descriptionformat,
1322 $context->id, 'coursecat', 'description', null);
3ec163dd
EL
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;
3dc1d76e 1328
3ec163dd
EL
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;
3dc1d76e 1336 }
3dc1d76e 1337
3ec163dd
EL
1338 $categoriesinfo[] = $categoryinfo;
1339 } else {
1340 $excludedcats[$category->id] = 'visibility';
1341 }
3dc1d76e
JL
1342 }
1343 }
1344
3ec163dd
EL
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");
3dc1d76e 1347
3ec163dd
EL
1348 return $categoriesinfo;
1349 }
3dc1d76e 1350
3ec163dd
EL
1351 /**
1352 * Sort categories array by path
1353 * private function: only used by get_categories
1354 *
1355 * @param array $category1
1356 * @param array $category2
1357 * @return int result of strcmp
1358 * @since Moodle 2.3
1359 */
1360 private static function compare_categories_by_path($category1, $category2) {
1361 return strcmp($category1->path, $category2->path);
1362 }
6c7d3e31 1363
3ec163dd
EL
1364 /**
1365 * Sort categories array by sortorder
1366 * private function: only used by get_categories
1367 *
1368 * @param array $category1
1369 * @param array $category2
1370 * @return int result of strcmp
1371 * @since Moodle 2.3
1372 */
1373 private static function compare_categories_by_sortorder($category1, $category2) {
1374 return strcmp($category1['sortorder'], $category2['sortorder']);
3dc1d76e
JL
1375 }
1376
1377 /**
1378 * Returns description of method result value
1379 *
1380 * @return external_description
1381 * @since Moodle 2.3
1382 */
3ec163dd
EL
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'),
93ce0e82 1391 'descriptionformat' => new external_format_value('description'),
3ec163dd
EL
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'
3dc1d76e
JL
1402 )
1403 );
1404 }
1405
2f951d86
FS
1406 /**
1407 * Returns description of method parameters
3ec163dd 1408 *
2f951d86
FS
1409 * @return external_function_parameters
1410 * @since Moodle 2.3
1411 */
3ec163dd 1412 public static function create_categories_parameters() {
2f951d86
FS
1413 return new external_function_parameters(
1414 array(
1415 'categories' => new external_multiple_structure(
3ec163dd
EL
1416 new external_single_structure(
1417 array(
1418 'name' => new external_value(PARAM_TEXT, 'new category name'),
1419 'parent' => new external_value(PARAM_INT,
9615b623
JM
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),
3ec163dd
EL
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),
93ce0e82 1427 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
3ec163dd
EL
1428 'theme' => new external_value(PARAM_THEME,
1429 'the new category theme. This option must be enabled on moodle',
1430 VALUE_OPTIONAL),
2f951d86
FS
1431 )
1432 )
1433 )
1434 )
1435 );
1436 }
1437
1438 /**
3ec163dd
EL
1439 * Create categories
1440 *
1441 * @param array $categories - see create_categories_parameters() for the array structure
1442 * @return array - see create_categories_returns() for the array structure
2f951d86
FS
1443 * @since Moodle 2.3
1444 */
3ec163dd 1445 public static function create_categories($categories) {
2f951d86
FS
1446 global $CFG, $DB;
1447 require_once($CFG->dirroot . "/course/lib.php");
1448
3ec163dd
EL
1449 $params = self::validate_parameters(self::create_categories_parameters(),
1450 array('categories' => $categories));
2f951d86 1451
3ec163dd
EL
1452 $transaction = $DB->start_delegated_transaction();
1453
1454 $createdcategories = array();
2f951d86 1455 foreach ($params['categories'] as $category) {
3ec163dd
EL
1456 if ($category['parent']) {
1457 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1458 throw new moodle_exception('unknowcategory');
1459 }
1460 $context = context_coursecat::instance($category['parent']);
1461 } else {
1462 $context = context_system::instance();
2f951d86 1463 }
2f951d86 1464 self::validate_context($context);
3ec163dd 1465 require_capability('moodle/category:manage', $context);
2f951d86 1466
3ec163dd
EL
1467 // Check name.
1468 if (textlib::strlen($category['name'])>255) {
1469 throw new moodle_exception('categorytoolong');
1470 }
2f951d86 1471
3ec163dd
EL
1472 $newcategory = new stdClass();
1473 $newcategory->name = $category['name'];
1474 $newcategory->parent = $category['parent'];
3ec163dd
EL
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'];
1479 }
93ce0e82 1480 $newcategory->descriptionformat = external_validate_format($category['descriptionformat']);
3ec163dd
EL
1481 if (isset($category['theme']) and !empty($CFG->allowcategorythemes)) {
1482 $newcategory->theme = $category['theme'];
2f951d86 1483 }
9615b623
JM
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');
1488 }
1489 if ($existing = $DB->get_record('course_categories', array('idnumber' => $category['idnumber']))) {
1490 if ($existing->id) {
1491 throw new moodle_exception('idnumbertaken');
1492 }
1493 }
1494 $newcategory->idnumber = $category['idnumber'];
1495 }
3ec163dd
EL
1496
1497 $newcategory = create_course_category($newcategory);
1498 // Populate special fields.
1499 fix_course_sortorder();
1500
1501 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
2f951d86
FS
1502 }
1503
3ec163dd
EL
1504 $transaction->allow_commit();
1505
1506 return $createdcategories;
2f951d86
FS
1507 }
1508
1509 /**
1510 * Returns description of method parameters
3ec163dd 1511 *
2f951d86
FS
1512 * @return external_function_parameters
1513 * @since Moodle 2.3
1514 */
3ec163dd
EL
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'),
1521 )
1522 )
1523 );
2f951d86 1524 }
f2229c68
FS
1525
1526 /**
1527 * Returns description of method parameters
3ec163dd 1528 *
f2229c68
FS
1529 * @return external_function_parameters
1530 * @since Moodle 2.3
1531 */
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),
93ce0e82 1543 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
f2229c68
FS
1544 'theme' => new external_value(PARAM_THEME,
1545 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1546 )
1547 )
1548 )
1549 )
1550 );
1551 }
1552
1553 /**
1554 * Update categories
3ec163dd 1555 *
f2229c68
FS
1556 * @param array $categories The list of categories to update
1557 * @return null
1558 * @since Moodle 2.3
1559 */
1560 public static function update_categories($categories) {
1561 global $CFG, $DB;
1562 require_once($CFG->dirroot . "/course/lib.php");
1563
1564 // Validate parameters.
1565 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1566
1567 $transaction = $DB->start_delegated_transaction();
1568
1569 foreach ($params['categories'] as $cat) {
1570 if (!$category = $DB->get_record('course_categories', array('id' => $cat['id']))) {
1571 throw new moodle_exception('unknowcategory');
1572 }
1573
1574 $categorycontext = context_coursecat::instance($cat['id']);
1575 self::validate_context($categorycontext);
1576 require_capability('moodle/category:manage', $categorycontext);
1577
1578 if (!empty($cat['name'])) {
d7238d08 1579 if (textlib::strlen($cat['name'])>255) {
f2229c68
FS
1580 throw new moodle_exception('categorytoolong');
1581 }
1582 $category->name = $cat['name'];
1583 }
1584 if (!empty($cat['idnumber'])) {
1585 if (textlib::strlen($cat['idnumber'])>100) {
1586 throw new moodle_exception('idnumbertoolong');
1587 }
1588 $category->idnumber = $cat['idnumber'];
1589 }
1590 if (!empty($cat['description'])) {
1591 $category->description = $cat['description'];
93ce0e82 1592 $category->descriptionformat = external_validate_format($cat['descriptionformat']);
f2229c68
FS
1593 }
1594 if (!empty($cat['theme'])) {
1595 $category->theme = $cat['theme'];
1596 }
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');
1601 }
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'];
fe77a34e
JM
1608 // Get updated path by move_category().
1609 $category->path = $DB->get_field('course_categories', 'path',
1610 array('id' => $category->id));
f2229c68
FS
1611 }
1612 $DB->update_record('course_categories', $category);
1613 }
1614
1615 $transaction->allow_commit();
1616 }
1617
1618 /**
1619 * Returns description of method result value
3ec163dd 1620 *
f2229c68 1621 * @return external_description
3ec163dd 1622 * @since Moodle 2.3
f2229c68
FS
1623 */
1624 public static function update_categories_returns() {
1625 return null;
1626 }
3ec163dd
EL
1627
1628 /**
1629 * Returns description of method parameters
1630 *
1631 * @return external_function_parameters
1632 * @since Moodle 2.3
1633 */
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)
1645 )
1646 )
1647 )
1648 )
1649 );
1650 }
1651
1652 /**
1653 * Delete categories
1654 *
1655 * @param array $categories A list of category ids
1656 * @return array
1657 * @since Moodle 2.3
1658 */
1659 public static function delete_categories($categories) {
1660 global $CFG, $DB;
1661 require_once($CFG->dirroot . "/course/lib.php");
1662
1663 // Validate parameters.
1664 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1665
f823158b
EL
1666 $transaction = $DB->start_delegated_transaction();
1667
3ec163dd
EL
1668 foreach ($params['categories'] as $category) {
1669 if (!$deletecat = $DB->get_record('course_categories', array('id' => $category['id']))) {
1670 throw new moodle_exception('unknowcategory');
1671 }
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));
1676
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');
1687 }
1688 $newparent = $category['newparent'];
1689 } else {
1690 $newparent = $deletecat->parent;
1691 }
1692
1693 // This operation is not allowed. We must move contents to an existing category.
1694 if ($newparent == 0) {
1695 throw new moodle_exception('movecatcontentstoroot');
1696 }
1697
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);
1702 }
1703 }
1704
f823158b 1705 $transaction->allow_commit();
3ec163dd
EL
1706 }
1707
1708 /**
1709 * Returns description of method parameters
1710 *
1711 * @return external_function_parameters
1712 * @since Moodle 2.3
1713 */
1714 public static function delete_categories_returns() {
1715 return null;
1716 }
1717
5d1017e1
JM
1718}
1719
1720/**
4615817d
JM
1721 * Deprecated course external functions
1722 *
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
5d1017e1
JM
1730 */
1731class moodle_course_external extends external_api {
1732
1733 /**
1734 * Returns description of method parameters
4615817d 1735 *
5d1017e1 1736 * @return external_function_parameters
4615817d
JM
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()
5d1017e1
JM
1741 */
1742 public static function get_courses_parameters() {
1743 return core_course_external::get_courses_parameters();
1744 }
1745
1746 /**
1747 * Get courses
4615817d 1748 *
5d1017e1 1749 * @param array $options
5d1017e1 1750 * @return array
4615817d
JM
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()
5d1017e1
JM
1755 */
1756 public static function get_courses($options) {
1757 return core_course_external::get_courses($options);
1758 }
1759
1760 /**
1761 * Returns description of method result value
4615817d 1762 *
5d1017e1 1763 * @return external_description
4615817d
JM
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()
5d1017e1
JM
1768 */
1769 public static function get_courses_returns() {
1770 return core_course_external::get_courses_returns();
1771 }
1772
1773 /**
1774 * Returns description of method parameters
4615817d 1775 *
5d1017e1 1776 * @return external_function_parameters
4615817d
JM
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()
5d1017e1
JM
1781 */
1782 public static function create_courses_parameters() {
1783 return core_course_external::create_courses_parameters();
1784 }
1785
1786 /**
1787 * Create courses
4615817d 1788 *
5d1017e1
JM
1789 * @param array $courses
1790 * @return array courses (id and shortname only)
4615817d
JM
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()
5d1017e1
JM
1795 */
1796 public static function create_courses($courses) {
1797 return core_course_external::create_courses($courses);
1798 }
1799
1800 /**
1801 * Returns description of method result value
4615817d 1802 *
5d1017e1 1803 * @return external_description
4615817d
JM
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()
5d1017e1
JM
1808 */
1809 public static function create_courses_returns() {
1810 return core_course_external::create_courses_returns();
1811 }
1812
ec0d6ea2 1813}