weekly release 3.0dev
[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
08b66e86 46 * @since Moodle 2.9 Options available
4615817d 47 * @since Moodle 2.2
ec0d6ea2
DC
48 */
49 public static function get_course_contents_parameters() {
50 return new external_function_parameters(
51 array('courseid' => new external_value(PARAM_INT, 'course id'),
52 'options' => new external_multiple_structure (
53 new external_single_structure(
08b66e86
JL
54 array(
55 'name' => new external_value(PARAM_ALPHANUM,
56 'The expected keys (value format) are:
57 excludemodules (bool) Do not return modules, return only the sections structure
58 excludecontents (bool) Do not return module contents (i.e: files inside a resource)
59 sectionid (int) Return only this section
60 sectionnumber (int) Return only this section with number (order)
61 cmid (int) Return only this module information (among the whole sections structure)
62 modname (string) Return only modules with this name "label, forum, etc..."
63 modid (int) Return only the module with this id (to be used with modname'),
64 'value' => new external_value(PARAM_RAW, 'the value of the option,
65 this param is personaly validated in the external function.')
ec0d6ea2 66 )
08b66e86 67 ), 'Options, used since Moodle 2.9', VALUE_DEFAULT, array())
ec0d6ea2
DC
68 )
69 );
70 }
71
72 /**
73 * Get course contents
4615817d
JM
74 *
75 * @param int $courseid course id
08b66e86 76 * @param array $options Options for filtering the results, used since Moodle 2.9
ec0d6ea2 77 * @return array
08b66e86 78 * @since Moodle 2.9 Options available
4615817d 79 * @since Moodle 2.2
ec0d6ea2 80 */
3297d575 81 public static function get_course_contents($courseid, $options = array()) {
ec0d6ea2
DC
82 global $CFG, $DB;
83 require_once($CFG->dirroot . "/course/lib.php");
84
85 //validate parameter
86 $params = self::validate_parameters(self::get_course_contents_parameters(),
87 array('courseid' => $courseid, 'options' => $options));
88
08b66e86
JL
89 $filters = array();
90 if (!empty($params['options'])) {
91
92 foreach ($params['options'] as $option) {
93 $name = trim($option['name']);
94 // Avoid duplicated options.
95 if (!isset($filters[$name])) {
96 switch ($name) {
97 case 'excludemodules':
98 case 'excludecontents':
99 $value = clean_param($option['value'], PARAM_BOOL);
100 $filters[$name] = $value;
101 break;
102 case 'sectionid':
103 case 'sectionnumber':
104 case 'cmid':
105 case 'modid':
106 $value = clean_param($option['value'], PARAM_INT);
107 if (is_numeric($value)) {
108 $filters[$name] = $value;
109 } else {
110 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
111 }
112 break;
113 case 'modname':
114 $value = clean_param($option['value'], PARAM_PLUGIN);
115 if ($value) {
116 $filters[$name] = $value;
117 } else {
118 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
119 }
120 break;
121 default:
122 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
123 }
124 }
125 }
126 }
127
ec0d6ea2
DC
128 //retrieve the course
129 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
130
12da294e
JL
131 if ($course->id != SITEID) {
132 // Check course format exist.
133 if (!file_exists($CFG->dirroot . '/course/format/' . $course->format . '/lib.php')) {
134 throw new moodle_exception('cannotgetcoursecontents', 'webservice', '', null,
135 get_string('courseformatnotfound', 'error', $course->format));
136 } else {
137 require_once($CFG->dirroot . '/course/format/' . $course->format . '/lib.php');
138 }
ec0d6ea2
DC
139 }
140
141 // now security checks
1f364c87 142 $context = context_course::instance($course->id, IGNORE_MISSING);
ec0d6ea2
DC
143 try {
144 self::validate_context($context);
145 } catch (Exception $e) {
146 $exceptionparam = new stdClass();
147 $exceptionparam->message = $e->getMessage();
148 $exceptionparam->courseid = $course->id;
149 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
150 }
151
152 $canupdatecourse = has_capability('moodle/course:update', $context);
153
154 //create return value
155 $coursecontents = array();
156
157 if ($canupdatecourse or $course->visible
158 or has_capability('moodle/course:viewhiddencourses', $context)) {
159
160 //retrieve sections
161 $modinfo = get_fast_modinfo($course);
71a56e08 162 $sections = $modinfo->get_section_info_all();
ec0d6ea2
DC
163
164 //for each sections (first displayed to last displayed)
9f3cc17d 165 $modinfosections = $modinfo->get_sections();
ec0d6ea2
DC
166 foreach ($sections as $key => $section) {
167
71a56e08 168 if (!$section->uservisible) {
ec0d6ea2
DC
169 continue;
170 }
171
08b66e86
JL
172 // This becomes true when we are filtering and we found the value to filter with.
173 $sectionfound = false;
174
175 // Filter by section id.
176 if (!empty($filters['sectionid'])) {
177 if ($section->id != $filters['sectionid']) {
178 continue;
179 } else {
180 $sectionfound = true;
181 }
182 }
183
184 // Filter by section number. Note that 0 is a valid section number.
185 if (isset($filters['sectionnumber'])) {
186 if ($key != $filters['sectionnumber']) {
187 continue;
188 } else {
189 $sectionfound = true;
190 }
191 }
192
ec0d6ea2
DC
193 // reset $sectioncontents
194 $sectionvalues = array();
195 $sectionvalues['id'] = $section->id;
196 $sectionvalues['name'] = get_section_name($course, $section);
ec0d6ea2 197 $sectionvalues['visible'] = $section->visible;
93ce0e82
JM
198 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
199 external_format_text($section->summary, $section->summaryformat,
200 $context->id, 'course', 'section', $section->id);
ec0d6ea2
DC
201 $sectioncontents = array();
202
203 //for each module of the section
08b66e86 204 if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) {
9f3cc17d
JM
205 foreach ($modinfosections[$section->section] as $cmid) {
206 $cm = $modinfo->cms[$cmid];
ec0d6ea2 207
9f3cc17d
JM
208 // stop here if the module is not visible to the user
209 if (!$cm->uservisible) {
210 continue;
211 }
ec0d6ea2 212
08b66e86
JL
213 // This becomes true when we are filtering and we found the value to filter with.
214 $modfound = false;
215
216 // Filter by cmid.
217 if (!empty($filters['cmid'])) {
218 if ($cmid != $filters['cmid']) {
219 continue;
220 } else {
221 $modfound = true;
222 }
223 }
224
225 // Filter by module name and id.
226 if (!empty($filters['modname'])) {
227 if ($cm->modname != $filters['modname']) {
228 continue;
229 } else if (!empty($filters['modid'])) {
230 if ($cm->instance != $filters['modid']) {
231 continue;
232 } else {
233 // Note that if we are only filtering by modname we don't break the loop.
234 $modfound = true;
235 }
236 }
237 }
238
9f3cc17d 239 $module = array();
ec0d6ea2 240
9f3cc17d
JM
241 //common info (for people being able to see the module or availability dates)
242 $module['id'] = $cm->id;
243 $module['name'] = format_string($cm->name, true);
d3549931 244 $module['instance'] = $cm->instance;
9f3cc17d
JM
245 $module['modname'] = $cm->modname;
246 $module['modplural'] = $cm->modplural;
247 $module['modicon'] = $cm->get_icon_url()->out(false);
248 $module['indent'] = $cm->indent;
ec0d6ea2 249
9f3cc17d 250 $modcontext = context_module::instance($cm->id);
ec0d6ea2 251
e7c5ee51 252 if (!empty($cm->showdescription) or $cm->modname == 'label') {
73ee2fda
DP
253 // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML.
254 list($module['description'], $descriptionformat) = external_format_text($cm->content,
e7c5ee51 255 FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id);
9f3cc17d 256 }
ec0d6ea2 257
9f3cc17d 258 //url of the module
73ee2fda 259 $url = $cm->url;
9f3cc17d 260 if ($url) { //labels don't have url
73ee2fda 261 $module['url'] = $url->out(false);
9f3cc17d 262 }
ec0d6ea2 263
9f3cc17d
JM
264 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
265 context_module::instance($cm->id));
266 //user that can view hidden module should know about the visibility
267 $module['visible'] = $cm->visible;
ec0d6ea2 268
8d1f33e1 269 // Availability date (also send to user who can see hidden module).
270 if ($CFG->enableavailability && ($canviewhidden || $canupdatecourse)) {
271 $module['availability'] = $cm->availability;
9f3cc17d 272 }
ec0d6ea2 273
9f3cc17d 274 $baseurl = 'webservice/pluginfile.php';
ec0d6ea2 275
9f3cc17d
JM
276 //call $modulename_export_contents
277 //(each module callback take care about checking the capabilities)
08b66e86 278
9f3cc17d
JM
279 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
280 $getcontentfunction = $cm->modname.'_export_contents';
281 if (function_exists($getcontentfunction)) {
08b66e86 282 if (empty($filters['excludecontents']) and $contents = $getcontentfunction($cm, $baseurl)) {
9f3cc17d 283 $module['contents'] = $contents;
08b66e86
JL
284 } else {
285 $module['contents'] = array();
9f3cc17d 286 }
ec0d6ea2 287 }
ec0d6ea2 288
9f3cc17d
JM
289 //assign result to $sectioncontents
290 $sectioncontents[] = $module;
ec0d6ea2 291
08b66e86
JL
292 // If we just did a filtering, break the loop.
293 if ($modfound) {
294 break;
295 }
296
9f3cc17d 297 }
ec0d6ea2
DC
298 }
299 $sectionvalues['modules'] = $sectioncontents;
300
301 // assign result to $coursecontents
302 $coursecontents[] = $sectionvalues;
08b66e86
JL
303
304 // Break the loop if we are filtering.
305 if ($sectionfound) {
306 break;
307 }
ec0d6ea2
DC
308 }
309 }
310 return $coursecontents;
311 }
312
313 /**
314 * Returns description of method result value
4615817d 315 *
ec0d6ea2 316 * @return external_description
4615817d 317 * @since Moodle 2.2
ec0d6ea2
DC
318 */
319 public static function get_course_contents_returns() {
320 return new external_multiple_structure(
321 new external_single_structure(
322 array(
323 'id' => new external_value(PARAM_INT, 'Section ID'),
324 'name' => new external_value(PARAM_TEXT, 'Section name'),
325 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
326 'summary' => new external_value(PARAM_RAW, 'Section description'),
93ce0e82 327 'summaryformat' => new external_format_value('summary'),
ec0d6ea2
DC
328 'modules' => new external_multiple_structure(
329 new external_single_structure(
330 array(
331 'id' => new external_value(PARAM_INT, 'activity id'),
332 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
11d81936 333 'name' => new external_value(PARAM_RAW, 'activity module name'),
ca4154ce 334 'instance' => new external_value(PARAM_INT, 'instance id', VALUE_OPTIONAL),
ec0d6ea2
DC
335 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
336 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
337 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
338 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
339 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
8d1f33e1 340 'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL),
ec0d6ea2
DC
341 'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
342 'contents' => new external_multiple_structure(
343 new external_single_structure(
344 array(
345 // content info
346 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
347 'filename'=> new external_value(PARAM_FILE, 'filename'),
348 'filepath'=> new external_value(PARAM_PATH, 'filepath'),
349 'filesize'=> new external_value(PARAM_INT, 'filesize'),
350 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
351 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
352 'timecreated' => new external_value(PARAM_INT, 'Time created'),
353 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
354 'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
355
356 // copyright related info
357 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
358 'author' => new external_value(PARAM_TEXT, 'Content owner'),
359 'license' => new external_value(PARAM_TEXT, 'Content license'),
360 )
361 ), VALUE_DEFAULT, array()
362 )
363 )
364 ), 'list of module'
365 )
366 )
367 )
368 );
369 }
370
6bb31e40 371 /**
372 * Returns description of method parameters
4615817d 373 *
6bb31e40 374 * @return external_function_parameters
754c2dea 375 * @since Moodle 2.3
6bb31e40 376 */
377 public static function get_courses_parameters() {
378 return new external_function_parameters(
379 array('options' => new external_single_structure(
380 array('ids' => new external_multiple_structure(
381 new external_value(PARAM_INT, 'Course id')
382 , 'List of course id. If empty return all courses
383 except front page course.',
384 VALUE_OPTIONAL)
385 ), 'options - operator OR is used', VALUE_DEFAULT, array())
386 )
387 );
388 }
389
390 /**
391 * Get courses
4615817d
JM
392 *
393 * @param array $options It contains an array (list of ids)
6bb31e40 394 * @return array
4615817d 395 * @since Moodle 2.2
6bb31e40 396 */
3297d575 397 public static function get_courses($options = array()) {
6bb31e40 398 global $CFG, $DB;
399 require_once($CFG->dirroot . "/course/lib.php");
400
401 //validate parameter
402 $params = self::validate_parameters(self::get_courses_parameters(),
403 array('options' => $options));
404
405 //retrieve courses
12fc8acf 406 if (!array_key_exists('ids', $params['options'])
6bb31e40 407 or empty($params['options']['ids'])) {
408 $courses = $DB->get_records('course');
409 } else {
410 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
411 }
412
413 //create return value
414 $coursesinfo = array();
415 foreach ($courses as $course) {
416
417 // now security checks
1f364c87 418 $context = context_course::instance($course->id, IGNORE_MISSING);
0e984d98 419 $courseformatoptions = course_get_format($course)->get_format_options();
6bb31e40 420 try {
421 self::validate_context($context);
422 } catch (Exception $e) {
423 $exceptionparam = new stdClass();
424 $exceptionparam->message = $e->getMessage();
425 $exceptionparam->courseid = $course->id;
96d3b93b 426 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
6bb31e40 427 }
428 require_capability('moodle/course:view', $context);
429
430 $courseinfo = array();
431 $courseinfo['id'] = $course->id;
432 $courseinfo['fullname'] = $course->fullname;
433 $courseinfo['shortname'] = $course->shortname;
434 $courseinfo['categoryid'] = $course->category;
93ce0e82
JM
435 list($courseinfo['summary'], $courseinfo['summaryformat']) =
436 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
6bb31e40 437 $courseinfo['format'] = $course->format;
438 $courseinfo['startdate'] = $course->startdate;
0e984d98
MG
439 if (array_key_exists('numsections', $courseformatoptions)) {
440 // For backward-compartibility
441 $courseinfo['numsections'] = $courseformatoptions['numsections'];
442 }
6bb31e40 443
444 //some field should be returned only if the user has update permission
445 $courseadmin = has_capability('moodle/course:update', $context);
446 if ($courseadmin) {
447 $courseinfo['categorysortorder'] = $course->sortorder;
448 $courseinfo['idnumber'] = $course->idnumber;
449 $courseinfo['showgrades'] = $course->showgrades;
450 $courseinfo['showreports'] = $course->showreports;
451 $courseinfo['newsitems'] = $course->newsitems;
452 $courseinfo['visible'] = $course->visible;
453 $courseinfo['maxbytes'] = $course->maxbytes;
0e984d98
MG
454 if (array_key_exists('hiddensections', $courseformatoptions)) {
455 // For backward-compartibility
456 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
457 }
6bb31e40 458 $courseinfo['groupmode'] = $course->groupmode;
459 $courseinfo['groupmodeforce'] = $course->groupmodeforce;
460 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
461 $courseinfo['lang'] = $course->lang;
462 $courseinfo['timecreated'] = $course->timecreated;
463 $courseinfo['timemodified'] = $course->timemodified;
464 $courseinfo['forcetheme'] = $course->theme;
465 $courseinfo['enablecompletion'] = $course->enablecompletion;
6bb31e40 466 $courseinfo['completionnotify'] = $course->completionnotify;
8d8d4da4 467 $courseinfo['courseformatoptions'] = array();
0e984d98 468 foreach ($courseformatoptions as $key => $value) {
8d8d4da4
MG
469 $courseinfo['courseformatoptions'][] = array(
470 'name' => $key,
471 'value' => $value
0e984d98
MG
472 );
473 }
6bb31e40 474 }
475
476 if ($courseadmin or $course->visible
477 or has_capability('moodle/course:viewhiddencourses', $context)) {
478 $coursesinfo[] = $courseinfo;
479 }
480 }
481
482 return $coursesinfo;
483 }
484
485 /**
486 * Returns description of method result value
4615817d 487 *
6bb31e40 488 * @return external_description
4615817d 489 * @since Moodle 2.2
6bb31e40 490 */
491 public static function get_courses_returns() {
492 return new external_multiple_structure(
493 new external_single_structure(
494 array(
495 'id' => new external_value(PARAM_INT, 'course id'),
496 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
497 'categoryid' => new external_value(PARAM_INT, 'category id'),
498 'categorysortorder' => new external_value(PARAM_INT,
499 'sort order into the category', VALUE_OPTIONAL),
500 'fullname' => new external_value(PARAM_TEXT, 'full name'),
501 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
502 'summary' => new external_value(PARAM_RAW, 'summary'),
93ce0e82 503 'summaryformat' => new external_format_value('summary'),
aff24313 504 'format' => new external_value(PARAM_PLUGIN,
6bb31e40 505 'course format: weeks, topics, social, site,..'),
506 'showgrades' => new external_value(PARAM_INT,
507 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
508 'newsitems' => new external_value(PARAM_INT,
509 'number of recent items appearing on the course page', VALUE_OPTIONAL),
510 'startdate' => new external_value(PARAM_INT,
511 'timestamp when the course start'),
0e984d98 512 'numsections' => new external_value(PARAM_INT,
8d8d4da4 513 '(deprecated, use courseformatoptions) number of weeks/topics',
0e984d98 514 VALUE_OPTIONAL),
6bb31e40 515 'maxbytes' => new external_value(PARAM_INT,
516 'largest size of file that can be uploaded into the course',
517 VALUE_OPTIONAL),
518 'showreports' => new external_value(PARAM_INT,
519 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
520 'visible' => new external_value(PARAM_INT,
521 '1: available to student, 0:not available', VALUE_OPTIONAL),
522 'hiddensections' => new external_value(PARAM_INT,
8d8d4da4 523 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
3ec163dd
EL
524 VALUE_OPTIONAL),
525 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
526 VALUE_OPTIONAL),
527 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
528 VALUE_OPTIONAL),
529 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
530 VALUE_OPTIONAL),
531 'timecreated' => new external_value(PARAM_INT,
532 'timestamp when the course have been created', VALUE_OPTIONAL),
533 'timemodified' => new external_value(PARAM_INT,
534 'timestamp when the course have been modified', VALUE_OPTIONAL),
535 'enablecompletion' => new external_value(PARAM_INT,
536 'Enabled, control via completion and activity settings. Disbaled,
537 not shown in activity settings.',
538 VALUE_OPTIONAL),
3ec163dd
EL
539 'completionnotify' => new external_value(PARAM_INT,
540 '1: yes 0: no', VALUE_OPTIONAL),
541 'lang' => new external_value(PARAM_SAFEDIR,
542 'forced course language', VALUE_OPTIONAL),
543 'forcetheme' => new external_value(PARAM_PLUGIN,
544 'name of the force theme', VALUE_OPTIONAL),
8d8d4da4 545 'courseformatoptions' => new external_multiple_structure(
0e984d98 546 new external_single_structure(
8d8d4da4
MG
547 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
548 'value' => new external_value(PARAM_RAW, 'course format option value')
0e984d98
MG
549 )),
550 'additional options for particular course format', VALUE_OPTIONAL
551 ),
3ec163dd 552 ), 'course'
479a5db1 553 )
479a5db1
FS
554 );
555 }
556
6bb31e40 557 /**
558 * Returns description of method parameters
4615817d 559 *
6bb31e40 560 * @return external_function_parameters
4615817d 561 * @since Moodle 2.2
6bb31e40 562 */
563 public static function create_courses_parameters() {
564 $courseconfig = get_config('moodlecourse'); //needed for many default values
565 return new external_function_parameters(
566 array(
567 'courses' => new external_multiple_structure(
568 new external_single_structure(
569 array(
570 'fullname' => new external_value(PARAM_TEXT, 'full name'),
571 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
572 'categoryid' => new external_value(PARAM_INT, 'category id'),
573 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
574 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
93ce0e82 575 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
aff24313 576 'format' => new external_value(PARAM_PLUGIN,
6bb31e40 577 'course format: weeks, topics, social, site,..',
578 VALUE_DEFAULT, $courseconfig->format),
579 'showgrades' => new external_value(PARAM_INT,
580 '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
581 $courseconfig->showgrades),
582 'newsitems' => new external_value(PARAM_INT,
583 'number of recent items appearing on the course page',
584 VALUE_DEFAULT, $courseconfig->newsitems),
585 'startdate' => new external_value(PARAM_INT,
586 'timestamp when the course start', VALUE_OPTIONAL),
0e984d98 587 'numsections' => new external_value(PARAM_INT,
8d8d4da4 588 '(deprecated, use courseformatoptions) number of weeks/topics',
0e984d98 589 VALUE_OPTIONAL),
6bb31e40 590 'maxbytes' => new external_value(PARAM_INT,
591 'largest size of file that can be uploaded into the course',
592 VALUE_DEFAULT, $courseconfig->maxbytes),
593 'showreports' => new external_value(PARAM_INT,
594 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
595 $courseconfig->showreports),
596 'visible' => new external_value(PARAM_INT,
597 '1: available to student, 0:not available', VALUE_OPTIONAL),
598 'hiddensections' => new external_value(PARAM_INT,
8d8d4da4 599 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
0e984d98 600 VALUE_OPTIONAL),
6bb31e40 601 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
602 VALUE_DEFAULT, $courseconfig->groupmode),
603 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
604 VALUE_DEFAULT, $courseconfig->groupmodeforce),
605 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
606 VALUE_DEFAULT, 0),
607 'enablecompletion' => new external_value(PARAM_INT,
8a6b1193 608 'Enabled, control via completion and activity settings. Disabled,
6bb31e40 609 not shown in activity settings.',
610 VALUE_OPTIONAL),
6bb31e40 611 'completionnotify' => new external_value(PARAM_INT,
612 '1: yes 0: no', VALUE_OPTIONAL),
aff24313 613 'lang' => new external_value(PARAM_SAFEDIR,
6bb31e40 614 'forced course language', VALUE_OPTIONAL),
aff24313 615 'forcetheme' => new external_value(PARAM_PLUGIN,
6bb31e40 616 'name of the force theme', VALUE_OPTIONAL),
8d8d4da4 617 'courseformatoptions' => new external_multiple_structure(
0e984d98 618 new external_single_structure(
8d8d4da4
MG
619 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
620 'value' => new external_value(PARAM_RAW, 'course format option value')
0e984d98
MG
621 )),
622 'additional options for particular course format', VALUE_OPTIONAL),
6bb31e40 623 )
624 ), 'courses to create'
625 )
626 )
627 );
628 }
629
630 /**
631 * Create courses
4615817d 632 *
6bb31e40 633 * @param array $courses
634 * @return array courses (id and shortname only)
4615817d 635 * @since Moodle 2.2
6bb31e40 636 */
637 public static function create_courses($courses) {
638 global $CFG, $DB;
639 require_once($CFG->dirroot . "/course/lib.php");
640 require_once($CFG->libdir . '/completionlib.php');
641
6bb31e40 642 $params = self::validate_parameters(self::create_courses_parameters(),
643 array('courses' => $courses));
644
bd3b3bba 645 $availablethemes = core_component::get_plugin_list('theme');
6bb31e40 646 $availablelangs = get_string_manager()->get_list_of_translations();
647
648 $transaction = $DB->start_delegated_transaction();
649
650 foreach ($params['courses'] as $course) {
651
652 // Ensure the current user is allowed to run this function
1f364c87 653 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
6bb31e40 654 try {
655 self::validate_context($context);
656 } catch (Exception $e) {
657 $exceptionparam = new stdClass();
658 $exceptionparam->message = $e->getMessage();
659 $exceptionparam->catid = $course['categoryid'];
96d3b93b 660 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
6bb31e40 661 }
662 require_capability('moodle/course:create', $context);
663
664 // Make sure lang is valid
12fc8acf 665 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
96d3b93b 666 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
6bb31e40 667 }
668
669 // Make sure theme is valid
12fc8acf 670 if (array_key_exists('forcetheme', $course)) {
6bb31e40 671 if (!empty($CFG->allowcoursethemes)) {
672 if (empty($availablethemes[$course['forcetheme']])) {
96d3b93b 673 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
6bb31e40 674 } else {
675 $course['theme'] = $course['forcetheme'];
676 }
677 }
678 }
679
680 //force visibility if ws user doesn't have the permission to set it
681 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
682 if (!has_capability('moodle/course:visibility', $context)) {
683 $course['visible'] = $category->visible;
684 }
685
686 //set default value for completion
8a6b1193 687 $courseconfig = get_config('moodlecourse');
6bb31e40 688 if (completion_info::is_enabled_for_site()) {
12fc8acf 689 if (!array_key_exists('enablecompletion', $course)) {
8a6b1193 690 $course['enablecompletion'] = $courseconfig->enablecompletion;
6bb31e40 691 }
6bb31e40 692 } else {
693 $course['enablecompletion'] = 0;
6bb31e40 694 }
695
696 $course['category'] = $course['categoryid'];
697
93ce0e82
JM
698 // Summary format.
699 $course['summaryformat'] = external_validate_format($course['summaryformat']);
700
8d8d4da4
MG
701 if (!empty($course['courseformatoptions'])) {
702 foreach ($course['courseformatoptions'] as $option) {
703 $course[$option['name']] = $option['value'];
0e984d98
MG
704 }
705 }
706
6bb31e40 707 //Note: create_course() core function check shortname, idnumber, category
708 $course['id'] = create_course((object) $course)->id;
709
710 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
711 }
712
713 $transaction->allow_commit();
714
715 return $resultcourses;
716 }
717
718 /**
719 * Returns description of method result value
4615817d 720 *
6bb31e40 721 * @return external_description
4615817d 722 * @since Moodle 2.2
6bb31e40 723 */
724 public static function create_courses_returns() {
725 return new external_multiple_structure(
726 new external_single_structure(
727 array(
728 'id' => new external_value(PARAM_INT, 'course id'),
729 'shortname' => new external_value(PARAM_TEXT, 'short name'),
730 )
731 )
732 );
733 }
734
791723c3
RT
735 /**
736 * Update courses
737 *
738 * @return external_function_parameters
739 * @since Moodle 2.5
740 */
741 public static function update_courses_parameters() {
742 return new external_function_parameters(
743 array(
744 'courses' => new external_multiple_structure(
745 new external_single_structure(
746 array(
747 'id' => new external_value(PARAM_INT, 'ID of the course'),
748 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL),
749 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL),
750 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL),
751 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
752 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
753 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
754 'format' => new external_value(PARAM_PLUGIN,
755 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
756 'showgrades' => new external_value(PARAM_INT,
757 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
758 'newsitems' => new external_value(PARAM_INT,
759 'number of recent items appearing on the course page', VALUE_OPTIONAL),
760 'startdate' => new external_value(PARAM_INT,
761 'timestamp when the course start', VALUE_OPTIONAL),
762 'numsections' => new external_value(PARAM_INT,
763 '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL),
764 'maxbytes' => new external_value(PARAM_INT,
765 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL),
766 'showreports' => new external_value(PARAM_INT,
767 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
768 'visible' => new external_value(PARAM_INT,
769 '1: available to student, 0:not available', VALUE_OPTIONAL),
770 'hiddensections' => new external_value(PARAM_INT,
e2adaaf7 771 '(deprecated, use courseformatoptions) How the hidden sections in the course are
791723c3
RT
772 displayed to students', VALUE_OPTIONAL),
773 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
774 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
775 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
776 'enablecompletion' => new external_value(PARAM_INT,
777 'Enabled, control via completion and activity settings. Disabled,
778 not shown in activity settings.', VALUE_OPTIONAL),
791723c3
RT
779 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
780 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL),
781 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL),
782 'courseformatoptions' => new external_multiple_structure(
783 new external_single_structure(
784 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
785 'value' => new external_value(PARAM_RAW, 'course format option value')
786 )),
787 'additional options for particular course format', VALUE_OPTIONAL),
788 )
789 ), 'courses to update'
790 )
791 )
792 );
793 }
794
795 /**
796 * Update courses
797 *
798 * @param array $courses
799 * @since Moodle 2.5
800 */
801 public static function update_courses($courses) {
802 global $CFG, $DB;
803 require_once($CFG->dirroot . "/course/lib.php");
804 $warnings = array();
805
806 $params = self::validate_parameters(self::update_courses_parameters(),
807 array('courses' => $courses));
808
bd3b3bba 809 $availablethemes = core_component::get_plugin_list('theme');
791723c3
RT
810 $availablelangs = get_string_manager()->get_list_of_translations();
811
812 foreach ($params['courses'] as $course) {
813 // Catch any exception while updating course and return as warning to user.
814 try {
815 // Ensure the current user is allowed to run this function.
816 $context = context_course::instance($course['id'], MUST_EXIST);
817 self::validate_context($context);
818
819 $oldcourse = course_get_format($course['id'])->get_course();
820
821 require_capability('moodle/course:update', $context);
822
823 // Check if user can change category.
824 if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) {
825 require_capability('moodle/course:changecategory', $context);
826 $course['category'] = $course['categoryid'];
827 }
828
829 // Check if the user can change fullname.
830 if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) {
831 require_capability('moodle/course:changefullname', $context);
832 }
833
5536a561 834 // Check if the user can change shortname.
791723c3
RT
835 if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
836 require_capability('moodle/course:changeshortname', $context);
791723c3
RT
837 }
838
5536a561 839 // Check if the user can change the idnumber.
791723c3
RT
840 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
841 require_capability('moodle/course:changeidnumber', $context);
791723c3
RT
842 }
843
844 // Check if user can change summary.
845 if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) {
846 require_capability('moodle/course:changesummary', $context);
847 }
848
849 // Summary format.
850 if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) {
851 require_capability('moodle/course:changesummary', $context);
852 $course['summaryformat'] = external_validate_format($course['summaryformat']);
853 }
854
855 // Check if user can change visibility.
856 if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) {
857 require_capability('moodle/course:visibility', $context);
858 }
859
860 // Make sure lang is valid.
861 if (array_key_exists('lang', $course) && empty($availablelangs[$course['lang']])) {
862 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
863 }
864
865 // Make sure theme is valid.
866 if (array_key_exists('forcetheme', $course)) {
867 if (!empty($CFG->allowcoursethemes)) {
868 if (empty($availablethemes[$course['forcetheme']])) {
869 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
870 } else {
871 $course['theme'] = $course['forcetheme'];
872 }
873 }
874 }
875
876 // Make sure completion is enabled before setting it.
8819a836 877 if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) {
791723c3 878 $course['enabledcompletion'] = 0;
791723c3
RT
879 }
880
881 // Make sure maxbytes are less then CFG->maxbytes.
882 if (array_key_exists('maxbytes', $course)) {
883 $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
884 }
885
886 if (!empty($course['courseformatoptions'])) {
887 foreach ($course['courseformatoptions'] as $option) {
888 if (isset($option['name']) && isset($option['value'])) {
889 $course[$option['name']] = $option['value'];
890 }
891 }
892 }
893
894 // Update course if user has all required capabilities.
895 update_course((object) $course);
896 } catch (Exception $e) {
897 $warning = array();
898 $warning['item'] = 'course';
899 $warning['itemid'] = $course['id'];
900 if ($e instanceof moodle_exception) {
901 $warning['warningcode'] = $e->errorcode;
902 } else {
903 $warning['warningcode'] = $e->getCode();
904 }
905 $warning['message'] = $e->getMessage();
906 $warnings[] = $warning;
907 }
908 }
909
910 $result = array();
911 $result['warnings'] = $warnings;
912 return $result;
913 }
914
915 /**
916 * Returns description of method result value
917 *
918 * @return external_description
919 * @since Moodle 2.5
920 */
921 public static function update_courses_returns() {
922 return new external_single_structure(
923 array(
924 'warnings' => new external_warnings()
925 )
926 );
927 }
928
63a85dc7
JL
929 /**
930 * Returns description of method parameters
3ec163dd 931 *
63a85dc7 932 * @return external_function_parameters
3ec163dd 933 * @since Moodle 2.2
63a85dc7
JL
934 */
935 public static function delete_courses_parameters() {
936 return new external_function_parameters(
937 array(
938 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
939 )
940 );
941 }
942
943 /**
944 * Delete courses
3ec163dd 945 *
63a85dc7 946 * @param array $courseids A list of course ids
3ec163dd 947 * @since Moodle 2.2
63a85dc7
JL
948 */
949 public static function delete_courses($courseids) {
950 global $CFG, $DB;
951 require_once($CFG->dirroot."/course/lib.php");
952
953 // Parameter validation.
954 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
955
70f37963 956 $warnings = array();
63a85dc7
JL
957
958 foreach ($params['courseids'] as $courseid) {
70f37963
JH
959 $course = $DB->get_record('course', array('id' => $courseid));
960
961 if ($course === false) {
962 $warnings[] = array(
963 'item' => 'course',
964 'itemid' => $courseid,
965 'warningcode' => 'unknowncourseidnumber',
966 'message' => 'Unknown course ID ' . $courseid
967 );
968 continue;
969 }
63a85dc7
JL
970
971 // Check if the context is valid.
972 $coursecontext = context_course::instance($course->id);
973 self::validate_context($coursecontext);
974
70f37963 975 // Check if the current user has permission.
63a85dc7 976 if (!can_delete_course($courseid)) {
70f37963
JH
977 $warnings[] = array(
978 'item' => 'course',
979 'itemid' => $courseid,
980 'warningcode' => 'cannotdeletecourse',
981 'message' => 'You do not have the permission to delete this course' . $courseid
982 );
983 continue;
63a85dc7
JL
984 }
985
70f37963
JH
986 if (delete_course($course, false) === false) {
987 $warnings[] = array(
988 'item' => 'course',
989 'itemid' => $courseid,
990 'warningcode' => 'cannotdeletecategorycourse',
991 'message' => 'Course ' . $courseid . ' failed to be deleted'
992 );
993 continue;
994 }
63a85dc7
JL
995 }
996
70f37963 997 fix_course_sortorder();
63a85dc7 998
70f37963 999 return array('warnings' => $warnings);
63a85dc7
JL
1000 }
1001
1002 /**
1003 * Returns description of method result value
3ec163dd 1004 *
63a85dc7 1005 * @return external_description
3ec163dd 1006 * @since Moodle 2.2
63a85dc7
JL
1007 */
1008 public static function delete_courses_returns() {
70f37963
JH
1009 return new external_single_structure(
1010 array(
1011 'warnings' => new external_warnings()
1012 )
1013 );
63a85dc7
JL
1014 }
1015
3dc1d76e
JL
1016 /**
1017 * Returns description of method parameters
1018 *
1019 * @return external_function_parameters
1020 * @since Moodle 2.3
1021 */
1022 public static function duplicate_course_parameters() {
1023 return new external_function_parameters(
1024 array(
1025 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
1026 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
1027 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
1028 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
1029 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
1030 'options' => new external_multiple_structure(
1031 new external_single_structure(
1032 array(
3dfc29e1 1033 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
9aa84e91
JL
1034 "activities" (int) Include course activites (default to 1 that is equal to yes),
1035 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1036 "filters" (int) Include course filters (default to 1 that is equal to yes),
1037 "users" (int) Include users (default to 0 that is equal to no),
1038 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
9aa84e91 1039 "comments" (int) Include user comments (default to 0 that is equal to no),
7469c512 1040 "userscompletion" (int) Include user course completion information (default to 0 that is equal to no),
9aa84e91 1041 "logs" (int) Include course logs (default to 0 that is equal to no),
7469c512 1042 "grade_histories" (int) Include histories (default to 0 that is equal to no)'
9aa84e91
JL
1043 ),
1044 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1045 )
3dc1d76e
JL
1046 )
1047 ), VALUE_DEFAULT, array()
1048 ),
1049 )
1050 );
1051 }
1052
1053 /**
1054 * Duplicate a course
1055 *
1056 * @param int $courseid
1057 * @param string $fullname Duplicated course fullname
1058 * @param string $shortname Duplicated course shortname
1059 * @param int $categoryid Duplicated course parent category id
1060 * @param int $visible Duplicated course availability
1061 * @param array $options List of backup options
1062 * @return array New course info
1063 * @since Moodle 2.3
1064 */
3297d575 1065 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
3dc1d76e
JL
1066 global $CFG, $USER, $DB;
1067 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1068 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1069
1070 // Parameter validation.
1071 $params = self::validate_parameters(
1072 self::duplicate_course_parameters(),
1073 array(
1074 'courseid' => $courseid,
1075 'fullname' => $fullname,
1076 'shortname' => $shortname,
1077 'categoryid' => $categoryid,
1078 'visible' => $visible,
1079 'options' => $options
1080 )
1081 );
1082
3ec163dd
EL
1083 // Context validation.
1084
1085 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
19a86468 1086 throw new moodle_exception('invalidcourseid', 'error');
3ec163dd
EL
1087 }
1088
1089 // Category where duplicated course is going to be created.
1090 $categorycontext = context_coursecat::instance($params['categoryid']);
1091 self::validate_context($categorycontext);
1092
1093 // Course to be duplicated.
1094 $coursecontext = context_course::instance($course->id);
1095 self::validate_context($coursecontext);
1096
1097 $backupdefaults = array(
1098 'activities' => 1,
1099 'blocks' => 1,
1100 'filters' => 1,
1101 'users' => 0,
1102 'role_assignments' => 0,
3ec163dd 1103 'comments' => 0,
7469c512 1104 'userscompletion' => 0,
3ec163dd 1105 'logs' => 0,
7469c512 1106 'grade_histories' => 0
3ec163dd
EL
1107 );
1108
1109 $backupsettings = array();
1110 // Check for backup and restore options.
1111 if (!empty($params['options'])) {
1112 foreach ($params['options'] as $option) {
1113
1114 // Strict check for a correct value (allways 1 or 0, true or false).
1115 $value = clean_param($option['value'], PARAM_INT);
1116
1117 if ($value !== 0 and $value !== 1) {
1118 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1119 }
1120
1121 if (!isset($backupdefaults[$option['name']])) {
1122 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1123 }
1124
1125 $backupsettings[$option['name']] = $value;
1126 }
1127 }
1128
1129 // Capability checking.
1130
1131 // The backup controller check for this currently, this may be redundant.
1132 require_capability('moodle/course:create', $categorycontext);
1133 require_capability('moodle/restore:restorecourse', $categorycontext);
1134 require_capability('moodle/backup:backupcourse', $coursecontext);
1135
1136 if (!empty($backupsettings['users'])) {
1137 require_capability('moodle/backup:userinfo', $coursecontext);
1138 require_capability('moodle/restore:userinfo', $categorycontext);
1139 }
1140
1141 // Check if the shortname is used.
1142 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
1143 foreach ($foundcourses as $foundcourse) {
1144 $foundcoursenames[] = $foundcourse->fullname;
1145 }
1146
1147 $foundcoursenamestring = implode(',', $foundcoursenames);
1148 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1149 }
1150
1151 // Backup the course.
1152
1153 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1154 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
1155
1156 foreach ($backupsettings as $name => $value) {
1157 $bc->get_plan()->get_setting($name)->set_value($value);
1158 }
1159
1160 $backupid = $bc->get_backupid();
1161 $backupbasepath = $bc->get_plan()->get_basepath();
1162
1163 $bc->execute_plan();
1164 $results = $bc->get_results();
1165 $file = $results['backup_destination'];
1166
1167 $bc->destroy();
1168
1169 // Restore the backup immediately.
1170
1171 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
1172 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
00219425 1173 $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath);
3ec163dd
EL
1174 }
1175
1176 // Create new course.
1177 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1178
1179 $rc = new restore_controller($backupid, $newcourseid,
1180 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
1181
1182 foreach ($backupsettings as $name => $value) {
1183 $setting = $rc->get_plan()->get_setting($name);
1184 if ($setting->get_status() == backup_setting::NOT_LOCKED) {
1185 $setting->set_value($value);
1186 }
1187 }
1188
1189 if (!$rc->execute_precheck()) {
1190 $precheckresults = $rc->get_precheck_results();
1191 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1192 if (empty($CFG->keeptempdirectoriesonbackup)) {
1193 fulldelete($backupbasepath);
1194 }
1195
1196 $errorinfo = '';
1197
1198 foreach ($precheckresults['errors'] as $error) {
1199 $errorinfo .= $error;
1200 }
1201
1202 if (array_key_exists('warnings', $precheckresults)) {
1203 foreach ($precheckresults['warnings'] as $warning) {
1204 $errorinfo .= $warning;
1205 }
1206 }
1207
1208 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1209 }
1210 }
1211
1212 $rc->execute_plan();
1213 $rc->destroy();
1214
1215 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
1216 $course->fullname = $params['fullname'];
1217 $course->shortname = $params['shortname'];
1218 $course->visible = $params['visible'];
1219
1220 // Set shortname and fullname back.
1221 $DB->update_record('course', $course);
1222
1223 if (empty($CFG->keeptempdirectoriesonbackup)) {
1224 fulldelete($backupbasepath);
1225 }
1226
1227 // Delete the course backup file created by this WebService. Originally located in the course backups area.
1228 $file->delete();
1229
1230 return array('id' => $course->id, 'shortname' => $course->shortname);
1231 }
1232
1233 /**
1234 * Returns description of method result value
1235 *
1236 * @return external_description
1237 * @since Moodle 2.3
1238 */
1239 public static function duplicate_course_returns() {
1240 return new external_single_structure(
1241 array(
1242 'id' => new external_value(PARAM_INT, 'course id'),
1243 'shortname' => new external_value(PARAM_TEXT, 'short name'),
1244 )
1245 );
1246 }
1247
8430d87b 1248 /**
c1483c9c 1249 * Returns description of method parameters for import_course
8430d87b
JL
1250 *
1251 * @return external_function_parameters
c1483c9c 1252 * @since Moodle 2.4
8430d87b
JL
1253 */
1254 public static function import_course_parameters() {
1255 return new external_function_parameters(
1256 array(
1257 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
1258 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
1259 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
1260 'options' => new external_multiple_structure(
1261 new external_single_structure(
1262 array(
1263 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
1264 "activities" (int) Include course activites (default to 1 that is equal to yes),
1265 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1266 "filters" (int) Include course filters (default to 1 that is equal to yes)'
1267 ),
1268 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1269 )
1270 )
1271 ), VALUE_DEFAULT, array()
1272 ),
1273 )
1274 );
1275 }
1276
1277 /**
1278 * Imports a course
1279 *
1280 * @param int $importfrom The id of the course we are importing from
1281 * @param int $importto The id of the course we are importing to
1282 * @param bool $deletecontent Whether to delete the course we are importing to content
1283 * @param array $options List of backup options
1284 * @return null
c1483c9c 1285 * @since Moodle 2.4
8430d87b 1286 */
b5bd42e8 1287 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
8430d87b
JL
1288 global $CFG, $USER, $DB;
1289 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1290 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1291
1292 // Parameter validation.
1293 $params = self::validate_parameters(
c1483c9c
SH
1294 self::import_course_parameters(),
1295 array(
1296 'importfrom' => $importfrom,
1297 'importto' => $importto,
1298 'deletecontent' => $deletecontent,
1299 'options' => $options
1300 )
8430d87b
JL
1301 );
1302
1303 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1b2f5493 1304 throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
8430d87b
JL
1305 }
1306
1307 // Context validation.
1308
1309 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
0b9a3d7a 1310 throw new moodle_exception('invalidcourseid', 'error');
8430d87b
JL
1311 }
1312
1313 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
0b9a3d7a 1314 throw new moodle_exception('invalidcourseid', 'error');
8430d87b
JL
1315 }
1316
1317 $importfromcontext = context_course::instance($importfrom->id);
1318 self::validate_context($importfromcontext);
1319
1320 $importtocontext = context_course::instance($importto->id);
1321 self::validate_context($importtocontext);
1322
1323 $backupdefaults = array(
c1483c9c
SH
1324 'activities' => 1,
1325 'blocks' => 1,
1326 'filters' => 1
8430d87b
JL
1327 );
1328
1329 $backupsettings = array();
1330
1331 // Check for backup and restore options.
1332 if (!empty($params['options'])) {
1333 foreach ($params['options'] as $option) {
1334
1335 // Strict check for a correct value (allways 1 or 0, true or false).
1336 $value = clean_param($option['value'], PARAM_INT);
1337
1338 if ($value !== 0 and $value !== 1) {
1339 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1340 }
1341
1342 if (!isset($backupdefaults[$option['name']])) {
1343 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1344 }
1345
1346 $backupsettings[$option['name']] = $value;
1347 }
1348 }
1349
1350 // Capability checking.
1351
1352 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1353 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1354
1355 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1356 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1357
1358 foreach ($backupsettings as $name => $value) {
1359 $bc->get_plan()->get_setting($name)->set_value($value);
1360 }
1361
1362 $backupid = $bc->get_backupid();
1363 $backupbasepath = $bc->get_plan()->get_basepath();
1364
1365 $bc->execute_plan();
1366 $bc->destroy();
1367
1368 // Restore the backup immediately.
1369
1370 // Check if we must delete the contents of the destination course.
1371 if ($params['deletecontent']) {
1372 $restoretarget = backup::TARGET_EXISTING_DELETING;
1373 } else {
1374 $restoretarget = backup::TARGET_EXISTING_ADDING;
1375 }
1376
1377 $rc = new restore_controller($backupid, $importto->id,
1378 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1379
1380 foreach ($backupsettings as $name => $value) {
1381 $rc->get_plan()->get_setting($name)->set_value($value);
1382 }
1383
1384 if (!$rc->execute_precheck()) {
1385 $precheckresults = $rc->get_precheck_results();
1386 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1387 if (empty($CFG->keeptempdirectoriesonbackup)) {
1388 fulldelete($backupbasepath);
1389 }
1390
1391 $errorinfo = '';
1392
1393 foreach ($precheckresults['errors'] as $error) {
1394 $errorinfo .= $error;
1395 }
1396
1397 if (array_key_exists('warnings', $precheckresults)) {
1398 foreach ($precheckresults['warnings'] as $warning) {
1399 $errorinfo .= $warning;
1400 }
1401 }
1402
1403 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1404 }
1405 } else {
1406 if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1407 restore_dbops::delete_course_content($importto->id);
1408 }
1409 }
1410
1411 $rc->execute_plan();
1412 $rc->destroy();
1413
1414 if (empty($CFG->keeptempdirectoriesonbackup)) {
1415 fulldelete($backupbasepath);
1416 }
1417
1418 return null;
1419 }
1420
1421 /**
1422 * Returns description of method result value
1423 *
1424 * @return external_description
c1483c9c 1425 * @since Moodle 2.4
8430d87b
JL
1426 */
1427 public static function import_course_returns() {
1428 return null;
1429 }
1430
3ec163dd
EL
1431 /**
1432 * Returns description of method parameters
1433 *
1434 * @return external_function_parameters
1435 * @since Moodle 2.3
1436 */
1437 public static function get_categories_parameters() {
1438 return new external_function_parameters(
1439 array(
1440 'criteria' => new external_multiple_structure(
1441 new external_single_structure(
1442 array(
1443 'key' => new external_value(PARAM_ALPHA,
1444 'The category column to search, expected keys (value format) are:'.
1445 '"id" (int) the category id,'.
1446 '"name" (string) the category name,'.
1447 '"parent" (int) the parent category id,'.
1448 '"idnumber" (string) category idnumber'.
1449 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
e6d1218a
JM
1450 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1451 then the function return all categories that the user can see.'.
3ec163dd 1452 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
e6d1218a 1453 '"theme" (string) only return the categories having this theme'.
3ec163dd
EL
1454 ' - user must have \'moodle/category:manage\' to search on theme'),
1455 'value' => new external_value(PARAM_RAW, 'the value to match')
1456 )
7a384506 1457 ), 'criteria', VALUE_DEFAULT, array()
3ec163dd
EL
1458 ),
1459 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1460 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1461 )
1462 );
1463 }
1464
1465 /**
1466 * Get categories
1467 *
1468 * @param array $criteria Criteria to match the results
1469 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1470 * @return array list of categories
1471 * @since Moodle 2.3
1472 */
1473 public static function get_categories($criteria = array(), $addsubcategories = true) {
1474 global $CFG, $DB;
1475 require_once($CFG->dirroot . "/course/lib.php");
1476
1477 // Validate parameters.
1478 $params = self::validate_parameters(self::get_categories_parameters(),
1479 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1480
1481 // Retrieve the categories.
1482 $categories = array();
1483 if (!empty($params['criteria'])) {
1484
1485 $conditions = array();
1486 $wheres = array();
1487 foreach ($params['criteria'] as $crit) {
1488 $key = trim($crit['key']);
1489
1490 // Trying to avoid duplicate keys.
1491 if (!isset($conditions[$key])) {
3dc1d76e 1492
3ec163dd
EL
1493 $context = context_system::instance();
1494 $value = null;
1495 switch ($key) {
1496 case 'id':
1497 $value = clean_param($crit['value'], PARAM_INT);
1498 break;
3dc1d76e 1499
3ec163dd
EL
1500 case 'idnumber':
1501 if (has_capability('moodle/category:manage', $context)) {
1502 $value = clean_param($crit['value'], PARAM_RAW);
1503 } else {
1504 // We must throw an exception.
1505 // Otherwise the dev client would think no idnumber exists.
1506 throw new moodle_exception('criteriaerror',
1507 'webservice', '', null,
1508 'You don\'t have the permissions to search on the "idnumber" field.');
1509 }
1510 break;
3dc1d76e 1511
3ec163dd
EL
1512 case 'name':
1513 $value = clean_param($crit['value'], PARAM_TEXT);
1514 break;
3dc1d76e 1515
3ec163dd
EL
1516 case 'parent':
1517 $value = clean_param($crit['value'], PARAM_INT);
1518 break;
9aa84e91 1519
3ec163dd
EL
1520 case 'visible':
1521 if (has_capability('moodle/category:manage', $context)
1522 or has_capability('moodle/category:viewhiddencategories',
1523 context_system::instance())) {
1524 $value = clean_param($crit['value'], PARAM_INT);
1525 } else {
1526 throw new moodle_exception('criteriaerror',
1527 'webservice', '', null,
1528 'You don\'t have the permissions to search on the "visible" field.');
1529 }
1530 break;
9aa84e91 1531
3ec163dd
EL
1532 case 'theme':
1533 if (has_capability('moodle/category:manage', $context)) {
1534 $value = clean_param($crit['value'], PARAM_THEME);
1535 } else {
1536 throw new moodle_exception('criteriaerror',
1537 'webservice', '', null,
1538 'You don\'t have the permissions to search on the "theme" field.');
1539 }
1540 break;
9aa84e91 1541
3ec163dd
EL
1542 default:
1543 throw new moodle_exception('criteriaerror',
1544 'webservice', '', null,
1545 'You can not search on this criteria: ' . $key);
1546 }
9aa84e91 1547
3ec163dd
EL
1548 if (isset($value)) {
1549 $conditions[$key] = $crit['value'];
1550 $wheres[] = $key . " = :" . $key;
1551 }
9aa84e91 1552 }
9aa84e91 1553 }
9aa84e91 1554
3ec163dd
EL
1555 if (!empty($wheres)) {
1556 $wheres = implode(" AND ", $wheres);
3dc1d76e 1557
3ec163dd 1558 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
3dc1d76e 1559
3ec163dd
EL
1560 // Retrieve its sub subcategories (all levels).
1561 if ($categories and !empty($params['addsubcategories'])) {
1562 $newcategories = array();
9aa84e91 1563
e6d1218a
JM
1564 // Check if we required visible/theme checks.
1565 $additionalselect = '';
1566 $additionalparams = array();
1567 if (isset($conditions['visible'])) {
1568 $additionalselect .= ' AND visible = :visible';
1569 $additionalparams['visible'] = $conditions['visible'];
1570 }
1571 if (isset($conditions['theme'])) {
1572 $additionalselect .= ' AND theme= :theme';
1573 $additionalparams['theme'] = $conditions['theme'];
1574 }
1575
3ec163dd 1576 foreach ($categories as $category) {
e6d1218a
JM
1577 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1578 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1579 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
3ec163dd
EL
1580 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1581 }
1582 $categories = $categories + $newcategories;
1583 }
3dc1d76e
JL
1584 }
1585
3ec163dd
EL
1586 } else {
1587 // Retrieve all categories in the database.
1588 $categories = $DB->get_records('course_categories');
3dc1d76e
JL
1589 }
1590
3ec163dd
EL
1591 // The not returned categories. key => category id, value => reason of exclusion.
1592 $excludedcats = array();
3dc1d76e 1593
3ec163dd
EL
1594 // The returned categories.
1595 $categoriesinfo = array();
6c7d3e31 1596
3ec163dd
EL
1597 // We need to sort the categories by path.
1598 // The parent cats need to be checked by the algo first.
1599 usort($categories, "core_course_external::compare_categories_by_path");
3dc1d76e 1600
3ec163dd 1601 foreach ($categories as $category) {
3dc1d76e 1602
3ec163dd
EL
1603 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1604 $parents = explode('/', $category->path);
1605 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1606 foreach ($parents as $parentid) {
1607 // Note: when the parent exclusion was due to the context,
1608 // the sub category could still be returned.
1609 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1610 $excludedcats[$category->id] = 'parent';
1611 }
1612 }
9aa84e91 1613
3ec163dd
EL
1614 // Check category depth is <= maxdepth (do not check for user who can manage categories).
1615 if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
1616 and !has_capability('moodle/category:manage', $context)) {
1617 $excludedcats[$category->id] = 'depth';
1618 }
3dc1d76e 1619
3ec163dd
EL
1620 // Check the user can use the category context.
1621 $context = context_coursecat::instance($category->id);
1622 try {
1623 self::validate_context($context);
1624 } catch (Exception $e) {
1625 $excludedcats[$category->id] = 'context';
3dc1d76e 1626
3ec163dd
EL
1627 // If it was the requested category then throw an exception.
1628 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1629 $exceptionparam = new stdClass();
1630 $exceptionparam->message = $e->getMessage();
1631 $exceptionparam->catid = $category->id;
1632 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1633 }
9aa84e91 1634 }
3dc1d76e 1635
3ec163dd
EL
1636 // Return the category information.
1637 if (!isset($excludedcats[$category->id])) {
3dc1d76e 1638
3ec163dd
EL
1639 // Final check to see if the category is visible to the user.
1640 if ($category->visible
1641 or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1642 or has_capability('moodle/category:manage', $context)) {
3dc1d76e 1643
3ec163dd
EL
1644 $categoryinfo = array();
1645 $categoryinfo['id'] = $category->id;
1646 $categoryinfo['name'] = $category->name;
93ce0e82
JM
1647 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1648 external_format_text($category->description, $category->descriptionformat,
1649 $context->id, 'coursecat', 'description', null);
3ec163dd
EL
1650 $categoryinfo['parent'] = $category->parent;
1651 $categoryinfo['sortorder'] = $category->sortorder;
1652 $categoryinfo['coursecount'] = $category->coursecount;
1653 $categoryinfo['depth'] = $category->depth;
1654 $categoryinfo['path'] = $category->path;
3dc1d76e 1655
3ec163dd
EL
1656 // Some fields only returned for admin.
1657 if (has_capability('moodle/category:manage', $context)) {
1658 $categoryinfo['idnumber'] = $category->idnumber;
1659 $categoryinfo['visible'] = $category->visible;
1660 $categoryinfo['visibleold'] = $category->visibleold;
1661 $categoryinfo['timemodified'] = $category->timemodified;
1662 $categoryinfo['theme'] = $category->theme;
3dc1d76e 1663 }
3dc1d76e 1664
3ec163dd
EL
1665 $categoriesinfo[] = $categoryinfo;
1666 } else {
1667 $excludedcats[$category->id] = 'visibility';
1668 }
3dc1d76e
JL
1669 }
1670 }
1671
3ec163dd
EL
1672 // Sorting the resulting array so it looks a bit better for the client developer.
1673 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
3dc1d76e 1674
3ec163dd
EL
1675 return $categoriesinfo;
1676 }
3dc1d76e 1677
3ec163dd
EL
1678 /**
1679 * Sort categories array by path
1680 * private function: only used by get_categories
1681 *
1682 * @param array $category1
1683 * @param array $category2
1684 * @return int result of strcmp
1685 * @since Moodle 2.3
1686 */
1687 private static function compare_categories_by_path($category1, $category2) {
1688 return strcmp($category1->path, $category2->path);
1689 }
6c7d3e31 1690
3ec163dd
EL
1691 /**
1692 * Sort categories array by sortorder
1693 * private function: only used by get_categories
1694 *
1695 * @param array $category1
1696 * @param array $category2
1697 * @return int result of strcmp
1698 * @since Moodle 2.3
1699 */
1700 private static function compare_categories_by_sortorder($category1, $category2) {
1701 return strcmp($category1['sortorder'], $category2['sortorder']);
3dc1d76e
JL
1702 }
1703
1704 /**
1705 * Returns description of method result value
1706 *
1707 * @return external_description
1708 * @since Moodle 2.3
1709 */
3ec163dd
EL
1710 public static function get_categories_returns() {
1711 return new external_multiple_structure(
1712 new external_single_structure(
1713 array(
1714 'id' => new external_value(PARAM_INT, 'category id'),
1715 'name' => new external_value(PARAM_TEXT, 'category name'),
1716 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1717 'description' => new external_value(PARAM_RAW, 'category description'),
93ce0e82 1718 'descriptionformat' => new external_format_value('description'),
3ec163dd
EL
1719 'parent' => new external_value(PARAM_INT, 'parent category id'),
1720 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1721 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1722 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1723 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1724 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1725 'depth' => new external_value(PARAM_INT, 'category depth'),
1726 'path' => new external_value(PARAM_TEXT, 'category path'),
1727 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1728 ), 'List of categories'
3dc1d76e
JL
1729 )
1730 );
1731 }
1732
2f951d86
FS
1733 /**
1734 * Returns description of method parameters
3ec163dd 1735 *
2f951d86
FS
1736 * @return external_function_parameters
1737 * @since Moodle 2.3
1738 */
3ec163dd 1739 public static function create_categories_parameters() {
2f951d86
FS
1740 return new external_function_parameters(
1741 array(
1742 'categories' => new external_multiple_structure(
3ec163dd
EL
1743 new external_single_structure(
1744 array(
1745 'name' => new external_value(PARAM_TEXT, 'new category name'),
1746 'parent' => new external_value(PARAM_INT,
9615b623
JM
1747 'the parent category id inside which the new category will be created
1748 - set to 0 for a root category',
1749 VALUE_DEFAULT, 0),
3ec163dd
EL
1750 'idnumber' => new external_value(PARAM_RAW,
1751 'the new category idnumber', VALUE_OPTIONAL),
1752 'description' => new external_value(PARAM_RAW,
1753 'the new category description', VALUE_OPTIONAL),
93ce0e82 1754 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
3ec163dd
EL
1755 'theme' => new external_value(PARAM_THEME,
1756 'the new category theme. This option must be enabled on moodle',
1757 VALUE_OPTIONAL),
2f951d86
FS
1758 )
1759 )
1760 )
1761 )
1762 );
1763 }
1764
1765 /**
3ec163dd
EL
1766 * Create categories
1767 *
1768 * @param array $categories - see create_categories_parameters() for the array structure
1769 * @return array - see create_categories_returns() for the array structure
2f951d86
FS
1770 * @since Moodle 2.3
1771 */
3ec163dd 1772 public static function create_categories($categories) {
2f951d86 1773 global $CFG, $DB;
9bad61db 1774 require_once($CFG->libdir . "/coursecatlib.php");
2f951d86 1775
3ec163dd
EL
1776 $params = self::validate_parameters(self::create_categories_parameters(),
1777 array('categories' => $categories));
2f951d86 1778
3ec163dd
EL
1779 $transaction = $DB->start_delegated_transaction();
1780
1781 $createdcategories = array();
2f951d86 1782 foreach ($params['categories'] as $category) {
3ec163dd
EL
1783 if ($category['parent']) {
1784 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1785 throw new moodle_exception('unknowcategory');
1786 }
1787 $context = context_coursecat::instance($category['parent']);
1788 } else {
1789 $context = context_system::instance();
2f951d86 1790 }
2f951d86 1791 self::validate_context($context);
3ec163dd 1792 require_capability('moodle/category:manage', $context);
2f951d86 1793
9bad61db
MG
1794 // this will validate format and throw an exception if there are errors
1795 external_validate_format($category['descriptionformat']);
3ec163dd 1796
9bad61db 1797 $newcategory = coursecat::create($category);
3ec163dd
EL
1798
1799 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
2f951d86
FS
1800 }
1801
3ec163dd
EL
1802 $transaction->allow_commit();
1803
1804 return $createdcategories;
2f951d86
FS
1805 }
1806
1807 /**
1808 * Returns description of method parameters
3ec163dd 1809 *
2f951d86
FS
1810 * @return external_function_parameters
1811 * @since Moodle 2.3
1812 */
3ec163dd
EL
1813 public static function create_categories_returns() {
1814 return new external_multiple_structure(
1815 new external_single_structure(
1816 array(
1817 'id' => new external_value(PARAM_INT, 'new category id'),
1818 'name' => new external_value(PARAM_TEXT, 'new category name'),
1819 )
1820 )
1821 );
2f951d86 1822 }
f2229c68
FS
1823
1824 /**
1825 * Returns description of method parameters
3ec163dd 1826 *
f2229c68
FS
1827 * @return external_function_parameters
1828 * @since Moodle 2.3
1829 */
1830 public static function update_categories_parameters() {
1831 return new external_function_parameters(
1832 array(
1833 'categories' => new external_multiple_structure(
1834 new external_single_structure(
1835 array(
1836 'id' => new external_value(PARAM_INT, 'course id'),
1837 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1838 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1839 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1840 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
93ce0e82 1841 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
f2229c68
FS
1842 'theme' => new external_value(PARAM_THEME,
1843 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1844 )
1845 )
1846 )
1847 )
1848 );
1849 }
1850
1851 /**
1852 * Update categories
3ec163dd 1853 *
f2229c68
FS
1854 * @param array $categories The list of categories to update
1855 * @return null
1856 * @since Moodle 2.3
1857 */
1858 public static function update_categories($categories) {
1859 global $CFG, $DB;
6e1d1ee0 1860 require_once($CFG->libdir . "/coursecatlib.php");
f2229c68
FS
1861
1862 // Validate parameters.
1863 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1864
1865 $transaction = $DB->start_delegated_transaction();
1866
1867 foreach ($params['categories'] as $cat) {
9bad61db 1868 $category = coursecat::get($cat['id']);
f2229c68
FS
1869
1870 $categorycontext = context_coursecat::instance($cat['id']);
1871 self::validate_context($categorycontext);
1872 require_capability('moodle/category:manage', $categorycontext);
1873
9bad61db
MG
1874 // this will throw an exception if descriptionformat is not valid
1875 external_validate_format($cat['descriptionformat']);
1876
1877 $category->update($cat);
f2229c68
FS
1878 }
1879
1880 $transaction->allow_commit();
1881 }
1882
1883 /**
1884 * Returns description of method result value
3ec163dd 1885 *
f2229c68 1886 * @return external_description
3ec163dd 1887 * @since Moodle 2.3
f2229c68
FS
1888 */
1889 public static function update_categories_returns() {
1890 return null;
1891 }
3ec163dd
EL
1892
1893 /**
1894 * Returns description of method parameters
1895 *
1896 * @return external_function_parameters
1897 * @since Moodle 2.3
1898 */
1899 public static function delete_categories_parameters() {
1900 return new external_function_parameters(
1901 array(
1902 'categories' => new external_multiple_structure(
1903 new external_single_structure(
1904 array(
1905 'id' => new external_value(PARAM_INT, 'category id to delete'),
1906 'newparent' => new external_value(PARAM_INT,
1907 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
1908 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
1909 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
1910 )
1911 )
1912 )
1913 )
1914 );
1915 }
1916
1917 /**
1918 * Delete categories
1919 *
1920 * @param array $categories A list of category ids
1921 * @return array
1922 * @since Moodle 2.3
1923 */
1924 public static function delete_categories($categories) {
1925 global $CFG, $DB;
1926 require_once($CFG->dirroot . "/course/lib.php");
deb65ced 1927 require_once($CFG->libdir . "/coursecatlib.php");
3ec163dd
EL
1928
1929 // Validate parameters.
1930 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1931
f823158b
EL
1932 $transaction = $DB->start_delegated_transaction();
1933
3ec163dd 1934 foreach ($params['categories'] as $category) {
deb65ced 1935 $deletecat = coursecat::get($category['id'], MUST_EXIST);
3ec163dd
EL
1936 $context = context_coursecat::instance($deletecat->id);
1937 require_capability('moodle/category:manage', $context);
1938 self::validate_context($context);
1939 self::validate_context(get_category_or_system_context($deletecat->parent));
1940
1941 if ($category['recursive']) {
1942 // If recursive was specified, then we recursively delete the category's contents.
deb65ced
MG
1943 if ($deletecat->can_delete_full()) {
1944 $deletecat->delete_full(false);
1945 } else {
1946 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1947 }
3ec163dd
EL
1948 } else {
1949 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1950 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1951 // We must move to an existing category.
1952 if (!empty($category['newparent'])) {
deb65ced 1953 $newparentcat = coursecat::get($category['newparent']);
3ec163dd 1954 } else {
deb65ced 1955 $newparentcat = coursecat::get($deletecat->parent);
3ec163dd
EL
1956 }
1957
1958 // This operation is not allowed. We must move contents to an existing category.
deb65ced 1959 if (!$newparentcat->id) {
3ec163dd
EL
1960 throw new moodle_exception('movecatcontentstoroot');
1961 }
1962
deb65ced
MG
1963 self::validate_context(context_coursecat::instance($newparentcat->id));
1964 if ($deletecat->can_move_content_to($newparentcat->id)) {
1965 $deletecat->delete_move($newparentcat->id, false);
1966 } else {
1967 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1968 }
3ec163dd
EL
1969 }
1970 }
1971
f823158b 1972 $transaction->allow_commit();
3ec163dd
EL
1973 }
1974
1975 /**
1976 * Returns description of method parameters
1977 *
1978 * @return external_function_parameters
1979 * @since Moodle 2.3
1980 */
1981 public static function delete_categories_returns() {
1982 return null;
1983 }
1984
79949c1b
MN
1985 /**
1986 * Describes the parameters for delete_modules.
1987 *
1988 * @return external_external_function_parameters
1989 * @since Moodle 2.5
1990 */
1991 public static function delete_modules_parameters() {
1992 return new external_function_parameters (
1993 array(
1994 'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID',
1995 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'),
1996 )
1997 );
1998 }
1999
2000 /**
2001 * Deletes a list of provided module instances.
2002 *
2003 * @param array $cmids the course module ids
2004 * @since Moodle 2.5
2005 */
2006 public static function delete_modules($cmids) {
2007 global $CFG, $DB;
2008
2009 // Require course file containing the course delete module function.
2010 require_once($CFG->dirroot . "/course/lib.php");
2011
2012 // Clean the parameters.
2013 $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids));
2014
2015 // Keep track of the course ids we have performed a capability check on to avoid repeating.
2016 $arrcourseschecked = array();
2017
2018 foreach ($params['cmids'] as $cmid) {
2019 // Get the course module.
2020 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST);
2021
2022 // Check if we have not yet confirmed they have permission in this course.
2023 if (!in_array($cm->course, $arrcourseschecked)) {
2024 // Ensure the current user has required permission in this course.
2025 $context = context_course::instance($cm->course);
2026 self::validate_context($context);
2027 // Add to the array.
2028 $arrcourseschecked[] = $cm->course;
2029 }
2030
2031 // Ensure they can delete this module.
2032 $modcontext = context_module::instance($cm->id);
2033 require_capability('moodle/course:manageactivities', $modcontext);
2034
2035 // Delete the module.
2036 course_delete_module($cm->id);
2037 }
2038 }
2039
2040 /**
2041 * Describes the delete_modules return value.
2042 *
2043 * @return external_single_structure
2044 * @since Moodle 2.5
2045 */
2046 public static function delete_modules_returns() {
2047 return null;
2048 }
7aed00d5
JL
2049
2050 /**
2051 * Returns description of method parameters
2052 *
2053 * @return external_function_parameters
2054 * @since Moodle 2.9
2055 */
2056 public static function view_course_parameters() {
2057 return new external_function_parameters(
2058 array(
2059 'courseid' => new external_value(PARAM_INT, 'id of the course'),
2060 'sectionnumber' => new external_value(PARAM_INT, 'section number', VALUE_DEFAULT, 0)
2061 )
2062 );
2063 }
2064
2065 /**
1c2b7882 2066 * Trigger the course viewed event.
7aed00d5
JL
2067 *
2068 * @param int $courseid id of course
2069 * @param int $sectionnumber sectionnumber (0, 1, 2...)
2070 * @return array of warnings and status result
2071 * @since Moodle 2.9
2072 * @throws moodle_exception
2073 */
2074 public static function view_course($courseid, $sectionnumber = 0) {
2075 global $CFG;
2076 require_once($CFG->dirroot . "/course/lib.php");
2077
2078 $params = self::validate_parameters(self::view_course_parameters(),
2079 array(
2080 'courseid' => $courseid,
2081 'sectionnumber' => $sectionnumber
2082 ));
2083
2084 $warnings = array();
2085
2086 $course = get_course($params['courseid']);
2087 $context = context_course::instance($course->id);
2088 self::validate_context($context);
2089
4dfef5e9
JL
2090 if (!empty($params['sectionnumber'])) {
2091
2092 // Get section details and check it exists.
2093 $modinfo = get_fast_modinfo($course);
2094 $coursesection = $modinfo->get_section_info($params['sectionnumber'], MUST_EXIST);
2095
2096 // Check user is allowed to see it.
2097 if (!$coursesection->uservisible) {
2098 require_capability('moodle/course:viewhiddensections', $context);
2099 }
2100 }
2101
7aed00d5
JL
2102 course_view($context, $params['sectionnumber']);
2103
2104 $result = array();
2105 $result['status'] = true;
2106 $result['warnings'] = $warnings;
2107 return $result;
2108 }
2109
2110 /**
2111 * Returns description of method result value
2112 *
2113 * @return external_description
2114 * @since Moodle 2.9
2115 */
2116 public static function view_course_returns() {
2117 return new external_single_structure(
2118 array(
2119 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2120 'warnings' => new external_warnings()
2121 )
2122 );
2123 }
2124
740c354f
JL
2125 /**
2126 * Returns description of method parameters
2127 *
2128 * @return external_function_parameters
2129 * @since Moodle 3.0
2130 */
2131 public static function search_courses_parameters() {
2132 return new external_function_parameters(
2133 array(
2134 'criterianame' => new external_value(PARAM_ALPHA, 'criteria name
2135 (search, modulelist (only admins), blocklist (only admins), tagid)'),
2136 'criteriavalue' => new external_value(PARAM_RAW, 'criteria value'),
2137 'page' => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0),
2138 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0)
2139 )
2140 );
2141 }
2142
2143 /**
2144 * Search courses following the specified criteria.
2145 *
2146 * @param string $criterianame Criteria name (search, modulelist (only admins), blocklist (only admins), tagid)
2147 * @param string $criteriavalue Criteria value
2148 * @param int $page Page number (for pagination)
2149 * @param int $perpage Items per page
2150 * @return array of course objects and warnings
2151 * @since Moodle 3.0
2152 * @throws moodle_exception
2153 */
2154 public static function search_courses($criterianame, $criteriavalue, $page=0, $perpage=0) {
2155 global $CFG;
2156 require_once($CFG->libdir . '/coursecatlib.php');
2157
2158 $warnings = array();
2159
2160 $parameters = array(
2161 'criterianame' => $criterianame,
2162 'criteriavalue' => $criteriavalue,
2163 'page' => $page,
2164 'perpage' => $perpage
2165 );
2166 $params = self::validate_parameters(self::search_courses_parameters(), $parameters);
2167
2168 $allowedcriterianames = array('search', 'modulelist', 'blocklist', 'tagid');
2169 if (!in_array($params['criterianame'], $allowedcriterianames)) {
2170 throw new invalid_parameter_exception('Invalid value for criterianame parameter (value: '.$params['criterianame'].'),' .
2171 'allowed values are: '.implode(',', $allowedcriterianames));
2172 }
2173
2174 if ($params['criterianame'] == 'modulelist' or $params['criterianame'] == 'blocklist') {
2175 require_capability('moodle/site:config', context_system::instance());
2176 }
2177
2178 $paramtype = array(
2179 'search' => PARAM_RAW,
2180 'modulelist' => PARAM_PLUGIN,
2181 'blocklist' => PARAM_INT,
2182 'tagid' => PARAM_INT
2183 );
2184 $params['criteriavalue'] = clean_param($params['criteriavalue'], $paramtype[$params['criterianame']]);
2185
2186 // Prepare the search API options.
2187 $searchcriteria = array();
2188 $searchcriteria[$params['criterianame']] = $params['criteriavalue'];
2189
2190 $options = array();
2191 if ($params['perpage'] != 0) {
2192 $offset = $params['page'] * $params['perpage'];
2193 $options = array('offset' => $offset, 'limit' => $params['perpage']);
2194 }
2195
2196 // Search the courses.
2197 $courses = coursecat::search_courses($searchcriteria, $options);
2198 $totalcount = coursecat::search_courses_count($searchcriteria);
2199
2200 $finalcourses = array();
2201 $categoriescache = array();
2202
2203 foreach ($courses as $course) {
2204
2205 $coursecontext = context_course::instance($course->id);
2206
2207 // Category information.
2208 if (!isset($categoriescache[$course->category])) {
2209 $categoriescache[$course->category] = coursecat::get($course->category);
2210 }
2211 $category = $categoriescache[$course->category];
2212
2213 // Retrieve course overfiew used files.
2214 $files = array();
2215 foreach ($course->get_course_overviewfiles() as $file) {
2216 $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
2217 $file->get_filearea(), null, $file->get_filepath(),
2218 $file->get_filename())->out(false);
2219 $files[] = array(
2220 'filename' => $file->get_filename(),
2221 'fileurl' => $fileurl,
2222 'filesize' => $file->get_filesize()
2223 );
2224 }
2225
2226 // Retrieve the course contacts,
2227 // we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services.
2228 $coursecontacts = array();
2229 foreach ($course->get_course_contacts() as $contact) {
2230 $coursecontacts[] = array(
2231 'id' => $contact['user']->id,
2232 'fullname' => $contact['username']
2233 );
2234 }
2235
2236 // Allowed enrolment methods (maybe we can self-enrol).
2237 $enroltypes = array();
2238 $instances = enrol_get_instances($course->id, true);
2239 foreach ($instances as $instance) {
2240 $enroltypes[] = $instance->enrol;
2241 }
2242
2243 // Format summary.
2244 list($summary, $summaryformat) =
2245 external_format_text($course->summary, $course->summaryformat, $coursecontext->id, 'course', 'summary', null);
2246
2247 $coursereturns = array();
2248 $coursereturns['id'] = $course->id;
2249 $coursereturns['fullname'] = $course->get_formatted_fullname();
2250 $coursereturns['shortname'] = $course->get_formatted_shortname();
2251 $coursereturns['categoryid'] = $course->category;
2252 $coursereturns['categoryname'] = $category->name;
2253 $coursereturns['summary'] = $summary;
2254 $coursereturns['summaryformat'] = $summaryformat;
2255 $coursereturns['overviewfiles'] = $files;
2256 $coursereturns['contacts'] = $coursecontacts;
2257 $coursereturns['enrollmentmethods'] = $enroltypes;
2258 $finalcourses[] = $coursereturns;
2259 }
2260
2261 return array(
2262 'total' => $totalcount,
2263 'courses' => $finalcourses,
2264 'warnings' => $warnings
2265 );
2266 }
2267
2268 /**
2269 * Returns description of method result value
2270 *
2271 * @return external_description
2272 * @since Moodle 3.0
2273 */
2274 public static function search_courses_returns() {
2275
2276 return new external_single_structure(
2277 array(
2278 'total' => new external_value(PARAM_INT, 'total course count'),
2279 'courses' => new external_multiple_structure(
2280 new external_single_structure(
2281 array(
2282 'id' => new external_value(PARAM_INT, 'course id'),
2283 'fullname' => new external_value(PARAM_TEXT, 'course full name'),
2284 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
2285 'categoryid' => new external_value(PARAM_INT, 'category id'),
2286 'categoryname' => new external_value(PARAM_TEXT, 'category name'),
2287 'summary' => new external_value(PARAM_RAW, 'summary'),
2288 'summaryformat' => new external_format_value('summary'),
2289 'overviewfiles' => new external_multiple_structure(
2290 new external_single_structure(
2291 array(
2292 'filename' => new external_value(PARAM_FILE, 'overview file name'),
2293 'fileurl' => new external_value(PARAM_URL, 'overview file url'),
2294 'filesize' => new external_value(PARAM_INT, 'overview file size'),
2295 )
2296 ),
2297 'additional overview files attached to this course'
2298 ),
2299 'contacts' => new external_multiple_structure(
2300 new external_single_structure(
2301 array(
2302 'id' => new external_value(PARAM_INT, 'contact user id'),
2303 'fullname' => new external_value(PARAM_NOTAGS, 'contact user fullname'),
2304 )
2305 ),
2306 'contact users'
2307 ),
2308 'enrollmentmethods' => new external_multiple_structure(
2309 new external_value(PARAM_PLUGIN, 'enrollment method'),
2310 'enrollment methods list'
2311 ),
2312 )
2313 ), 'course'
2314 ),
2315 'warnings' => new external_warnings()
2316 )
2317 );
2318 }
5d1017e1
JM
2319}
2320
2321/**
4615817d
JM
2322 * Deprecated course external functions
2323 *
2324 * @package core_course
2325 * @copyright 2009 Petr Skodak
2326 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2327 * @since Moodle 2.0
2328 * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
4615817d 2329 * @see core_course_external
5d1017e1
JM
2330 */
2331class moodle_course_external extends external_api {
2332
2333 /**
2334 * Returns description of method parameters
4615817d 2335 *
5d1017e1 2336 * @return external_function_parameters
4615817d
JM
2337 * @since Moodle 2.0
2338 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
4615817d 2339 * @see core_course_external::get_courses_parameters()
5d1017e1
JM
2340 */
2341 public static function get_courses_parameters() {
2342 return core_course_external::get_courses_parameters();
2343 }
2344
2345 /**
2346 * Get courses
4615817d 2347 *
5d1017e1 2348 * @param array $options
5d1017e1 2349 * @return array
4615817d
JM
2350 * @since Moodle 2.0
2351 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
4615817d 2352 * @see core_course_external::get_courses()
5d1017e1
JM
2353 */
2354 public static function get_courses($options) {
2355 return core_course_external::get_courses($options);
2356 }
2357
2358 /**
2359 * Returns description of method result value
4615817d 2360 *
5d1017e1 2361 * @return external_description
4615817d
JM
2362 * @since Moodle 2.0
2363 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
4615817d 2364 * @see core_course_external::get_courses_returns()
5d1017e1
JM
2365 */
2366 public static function get_courses_returns() {
2367 return core_course_external::get_courses_returns();
2368 }
2369
3c1aa6fd
DM
2370 /**
2371 * Marking the method as deprecated.
2372 *
2373 * @return bool
2374 */
2375 public static function get_courses_is_deprecated() {
2376 return true;
2377 }
2378
5d1017e1
JM
2379 /**
2380 * Returns description of method parameters
4615817d 2381 *
5d1017e1 2382 * @return external_function_parameters
4615817d
JM
2383 * @since Moodle 2.0
2384 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
4615817d 2385 * @see core_course_external::create_courses_parameters()
5d1017e1
JM
2386 */
2387 public static function create_courses_parameters() {
2388 return core_course_external::create_courses_parameters();
2389 }
2390
2391 /**
2392 * Create courses
4615817d 2393 *
5d1017e1
JM
2394 * @param array $courses
2395 * @return array courses (id and shortname only)
4615817d
JM
2396 * @since Moodle 2.0
2397 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
4615817d 2398 * @see core_course_external::create_courses()
5d1017e1
JM
2399 */
2400 public static function create_courses($courses) {
2401 return core_course_external::create_courses($courses);
2402 }
2403
2404 /**
2405 * Returns description of method result value
4615817d 2406 *
5d1017e1 2407 * @return external_description
4615817d
JM
2408 * @since Moodle 2.0
2409 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
4615817d 2410 * @see core_course_external::create_courses_returns()
5d1017e1
JM
2411 */
2412 public static function create_courses_returns() {
2413 return core_course_external::create_courses_returns();
2414 }
2415
3c1aa6fd
DM
2416 /**
2417 * Marking the method as deprecated.
2418 *
2419 * @return bool
2420 */
2421 public static function create_courses_is_deprecated() {
2422 return true;
2423 }
ec0d6ea2 2424}