MDL-56307 course: New course_check_module_updates_since method
[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;
6a1131e2
JL
198
199 $options = (object) array('noclean' => true);
93ce0e82
JM
200 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
201 external_format_text($section->summary, $section->summaryformat,
6a1131e2 202 $context->id, 'course', 'section', $section->id, $options);
9df9f1f0 203 $sectionvalues['section'] = $section->section;
ec0d6ea2
DC
204 $sectioncontents = array();
205
206 //for each module of the section
08b66e86 207 if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) {
9f3cc17d
JM
208 foreach ($modinfosections[$section->section] as $cmid) {
209 $cm = $modinfo->cms[$cmid];
ec0d6ea2 210
9f3cc17d
JM
211 // stop here if the module is not visible to the user
212 if (!$cm->uservisible) {
213 continue;
214 }
ec0d6ea2 215
08b66e86
JL
216 // This becomes true when we are filtering and we found the value to filter with.
217 $modfound = false;
218
219 // Filter by cmid.
220 if (!empty($filters['cmid'])) {
221 if ($cmid != $filters['cmid']) {
222 continue;
223 } else {
224 $modfound = true;
225 }
226 }
227
228 // Filter by module name and id.
229 if (!empty($filters['modname'])) {
230 if ($cm->modname != $filters['modname']) {
231 continue;
232 } else if (!empty($filters['modid'])) {
233 if ($cm->instance != $filters['modid']) {
234 continue;
235 } else {
236 // Note that if we are only filtering by modname we don't break the loop.
237 $modfound = true;
238 }
239 }
240 }
241
9f3cc17d 242 $module = array();
ec0d6ea2 243
9748791b
JL
244 $modcontext = context_module::instance($cm->id);
245
9f3cc17d
JM
246 //common info (for people being able to see the module or availability dates)
247 $module['id'] = $cm->id;
9748791b 248 $module['name'] = external_format_string($cm->name, $modcontext->id);
d3549931 249 $module['instance'] = $cm->instance;
9f3cc17d
JM
250 $module['modname'] = $cm->modname;
251 $module['modplural'] = $cm->modplural;
252 $module['modicon'] = $cm->get_icon_url()->out(false);
253 $module['indent'] = $cm->indent;
ec0d6ea2 254
e7c5ee51 255 if (!empty($cm->showdescription) or $cm->modname == 'label') {
73ee2fda
DP
256 // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML.
257 list($module['description'], $descriptionformat) = external_format_text($cm->content,
e7c5ee51 258 FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id);
9f3cc17d 259 }
ec0d6ea2 260
9f3cc17d 261 //url of the module
73ee2fda 262 $url = $cm->url;
9f3cc17d 263 if ($url) { //labels don't have url
73ee2fda 264 $module['url'] = $url->out(false);
9f3cc17d 265 }
ec0d6ea2 266
9f3cc17d
JM
267 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
268 context_module::instance($cm->id));
269 //user that can view hidden module should know about the visibility
270 $module['visible'] = $cm->visible;
ec0d6ea2 271
8d1f33e1 272 // Availability date (also send to user who can see hidden module).
273 if ($CFG->enableavailability && ($canviewhidden || $canupdatecourse)) {
274 $module['availability'] = $cm->availability;
9f3cc17d 275 }
ec0d6ea2 276
9f3cc17d 277 $baseurl = 'webservice/pluginfile.php';
ec0d6ea2 278
9f3cc17d
JM
279 //call $modulename_export_contents
280 //(each module callback take care about checking the capabilities)
08b66e86 281
9f3cc17d
JM
282 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
283 $getcontentfunction = $cm->modname.'_export_contents';
284 if (function_exists($getcontentfunction)) {
08b66e86 285 if (empty($filters['excludecontents']) and $contents = $getcontentfunction($cm, $baseurl)) {
9f3cc17d 286 $module['contents'] = $contents;
08b66e86
JL
287 } else {
288 $module['contents'] = array();
9f3cc17d 289 }
ec0d6ea2 290 }
ec0d6ea2 291
9f3cc17d
JM
292 //assign result to $sectioncontents
293 $sectioncontents[] = $module;
ec0d6ea2 294
08b66e86
JL
295 // If we just did a filtering, break the loop.
296 if ($modfound) {
297 break;
298 }
299
9f3cc17d 300 }
ec0d6ea2
DC
301 }
302 $sectionvalues['modules'] = $sectioncontents;
303
304 // assign result to $coursecontents
305 $coursecontents[] = $sectionvalues;
08b66e86
JL
306
307 // Break the loop if we are filtering.
308 if ($sectionfound) {
309 break;
310 }
ec0d6ea2
DC
311 }
312 }
313 return $coursecontents;
314 }
315
316 /**
317 * Returns description of method result value
4615817d 318 *
ec0d6ea2 319 * @return external_description
4615817d 320 * @since Moodle 2.2
ec0d6ea2
DC
321 */
322 public static function get_course_contents_returns() {
323 return new external_multiple_structure(
324 new external_single_structure(
325 array(
326 'id' => new external_value(PARAM_INT, 'Section ID'),
327 'name' => new external_value(PARAM_TEXT, 'Section name'),
328 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
329 'summary' => new external_value(PARAM_RAW, 'Section description'),
93ce0e82 330 'summaryformat' => new external_format_value('summary'),
9df9f1f0 331 'section' => new external_value(PARAM_INT, 'Section number inside the course', VALUE_OPTIONAL),
ec0d6ea2
DC
332 'modules' => new external_multiple_structure(
333 new external_single_structure(
334 array(
335 'id' => new external_value(PARAM_INT, 'activity id'),
336 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
11d81936 337 'name' => new external_value(PARAM_RAW, 'activity module name'),
ca4154ce 338 'instance' => new external_value(PARAM_INT, 'instance id', VALUE_OPTIONAL),
ec0d6ea2
DC
339 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
340 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
341 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
342 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
343 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
8d1f33e1 344 'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL),
ec0d6ea2
DC
345 'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
346 'contents' => new external_multiple_structure(
347 new external_single_structure(
348 array(
349 // content info
350 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
351 'filename'=> new external_value(PARAM_FILE, 'filename'),
352 'filepath'=> new external_value(PARAM_PATH, 'filepath'),
353 'filesize'=> new external_value(PARAM_INT, 'filesize'),
354 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
355 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
356 'timecreated' => new external_value(PARAM_INT, 'Time created'),
357 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
358 'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
359
360 // copyright related info
361 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
362 'author' => new external_value(PARAM_TEXT, 'Content owner'),
363 'license' => new external_value(PARAM_TEXT, 'Content license'),
364 )
365 ), VALUE_DEFAULT, array()
366 )
367 )
368 ), 'list of module'
369 )
370 )
371 )
372 );
373 }
374
6bb31e40 375 /**
376 * Returns description of method parameters
4615817d 377 *
6bb31e40 378 * @return external_function_parameters
754c2dea 379 * @since Moodle 2.3
6bb31e40 380 */
381 public static function get_courses_parameters() {
382 return new external_function_parameters(
383 array('options' => new external_single_structure(
384 array('ids' => new external_multiple_structure(
385 new external_value(PARAM_INT, 'Course id')
386 , 'List of course id. If empty return all courses
387 except front page course.',
388 VALUE_OPTIONAL)
389 ), 'options - operator OR is used', VALUE_DEFAULT, array())
390 )
391 );
392 }
393
394 /**
395 * Get courses
4615817d
JM
396 *
397 * @param array $options It contains an array (list of ids)
6bb31e40 398 * @return array
4615817d 399 * @since Moodle 2.2
6bb31e40 400 */
3297d575 401 public static function get_courses($options = array()) {
6bb31e40 402 global $CFG, $DB;
403 require_once($CFG->dirroot . "/course/lib.php");
404
405 //validate parameter
406 $params = self::validate_parameters(self::get_courses_parameters(),
407 array('options' => $options));
408
409 //retrieve courses
12fc8acf 410 if (!array_key_exists('ids', $params['options'])
6bb31e40 411 or empty($params['options']['ids'])) {
412 $courses = $DB->get_records('course');
413 } else {
414 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
415 }
416
417 //create return value
418 $coursesinfo = array();
419 foreach ($courses as $course) {
420
421 // now security checks
1f364c87 422 $context = context_course::instance($course->id, IGNORE_MISSING);
0e984d98 423 $courseformatoptions = course_get_format($course)->get_format_options();
6bb31e40 424 try {
425 self::validate_context($context);
426 } catch (Exception $e) {
427 $exceptionparam = new stdClass();
428 $exceptionparam->message = $e->getMessage();
429 $exceptionparam->courseid = $course->id;
96d3b93b 430 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
6bb31e40 431 }
432 require_capability('moodle/course:view', $context);
433
434 $courseinfo = array();
435 $courseinfo['id'] = $course->id;
d889b587
JL
436 $courseinfo['fullname'] = external_format_string($course->fullname, $context->id);
437 $courseinfo['shortname'] = external_format_string($course->shortname, $context->id);
440b4c54 438 $courseinfo['displayname'] = external_format_string(get_course_display_name_for_list($course), $context->id);
6bb31e40 439 $courseinfo['categoryid'] = $course->category;
93ce0e82
JM
440 list($courseinfo['summary'], $courseinfo['summaryformat']) =
441 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
6bb31e40 442 $courseinfo['format'] = $course->format;
443 $courseinfo['startdate'] = $course->startdate;
fbcdb0d7 444 $courseinfo['enddate'] = $course->enddate;
0e984d98
MG
445 if (array_key_exists('numsections', $courseformatoptions)) {
446 // For backward-compartibility
447 $courseinfo['numsections'] = $courseformatoptions['numsections'];
448 }
6bb31e40 449
450 //some field should be returned only if the user has update permission
451 $courseadmin = has_capability('moodle/course:update', $context);
452 if ($courseadmin) {
453 $courseinfo['categorysortorder'] = $course->sortorder;
454 $courseinfo['idnumber'] = $course->idnumber;
455 $courseinfo['showgrades'] = $course->showgrades;
456 $courseinfo['showreports'] = $course->showreports;
457 $courseinfo['newsitems'] = $course->newsitems;
458 $courseinfo['visible'] = $course->visible;
459 $courseinfo['maxbytes'] = $course->maxbytes;
0e984d98
MG
460 if (array_key_exists('hiddensections', $courseformatoptions)) {
461 // For backward-compartibility
462 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
463 }
6bb31e40 464 $courseinfo['groupmode'] = $course->groupmode;
465 $courseinfo['groupmodeforce'] = $course->groupmodeforce;
466 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
467 $courseinfo['lang'] = $course->lang;
468 $courseinfo['timecreated'] = $course->timecreated;
469 $courseinfo['timemodified'] = $course->timemodified;
470 $courseinfo['forcetheme'] = $course->theme;
471 $courseinfo['enablecompletion'] = $course->enablecompletion;
6bb31e40 472 $courseinfo['completionnotify'] = $course->completionnotify;
8d8d4da4 473 $courseinfo['courseformatoptions'] = array();
0e984d98 474 foreach ($courseformatoptions as $key => $value) {
8d8d4da4
MG
475 $courseinfo['courseformatoptions'][] = array(
476 'name' => $key,
477 'value' => $value
0e984d98
MG
478 );
479 }
6bb31e40 480 }
481
482 if ($courseadmin or $course->visible
483 or has_capability('moodle/course:viewhiddencourses', $context)) {
484 $coursesinfo[] = $courseinfo;
485 }
486 }
487
488 return $coursesinfo;
489 }
490
491 /**
492 * Returns description of method result value
4615817d 493 *
6bb31e40 494 * @return external_description
4615817d 495 * @since Moodle 2.2
6bb31e40 496 */
497 public static function get_courses_returns() {
498 return new external_multiple_structure(
499 new external_single_structure(
500 array(
501 'id' => new external_value(PARAM_INT, 'course id'),
502 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
503 'categoryid' => new external_value(PARAM_INT, 'category id'),
504 'categorysortorder' => new external_value(PARAM_INT,
505 'sort order into the category', VALUE_OPTIONAL),
506 'fullname' => new external_value(PARAM_TEXT, 'full name'),
440b4c54 507 'displayname' => new external_value(PARAM_TEXT, 'course display name'),
6bb31e40 508 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
509 'summary' => new external_value(PARAM_RAW, 'summary'),
93ce0e82 510 'summaryformat' => new external_format_value('summary'),
aff24313 511 'format' => new external_value(PARAM_PLUGIN,
6bb31e40 512 'course format: weeks, topics, social, site,..'),
513 'showgrades' => new external_value(PARAM_INT,
514 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
515 'newsitems' => new external_value(PARAM_INT,
516 'number of recent items appearing on the course page', VALUE_OPTIONAL),
517 'startdate' => new external_value(PARAM_INT,
518 'timestamp when the course start'),
fbcdb0d7
DNA
519 'enddate' => new external_value(PARAM_INT,
520 'timestamp when the course end'),
0e984d98 521 'numsections' => new external_value(PARAM_INT,
8d8d4da4 522 '(deprecated, use courseformatoptions) number of weeks/topics',
0e984d98 523 VALUE_OPTIONAL),
6bb31e40 524 'maxbytes' => new external_value(PARAM_INT,
525 'largest size of file that can be uploaded into the course',
526 VALUE_OPTIONAL),
527 'showreports' => new external_value(PARAM_INT,
528 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
529 'visible' => new external_value(PARAM_INT,
530 '1: available to student, 0:not available', VALUE_OPTIONAL),
531 'hiddensections' => new external_value(PARAM_INT,
8d8d4da4 532 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
3ec163dd
EL
533 VALUE_OPTIONAL),
534 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
535 VALUE_OPTIONAL),
536 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
537 VALUE_OPTIONAL),
538 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
539 VALUE_OPTIONAL),
540 'timecreated' => new external_value(PARAM_INT,
541 'timestamp when the course have been created', VALUE_OPTIONAL),
542 'timemodified' => new external_value(PARAM_INT,
543 'timestamp when the course have been modified', VALUE_OPTIONAL),
544 'enablecompletion' => new external_value(PARAM_INT,
545 'Enabled, control via completion and activity settings. Disbaled,
546 not shown in activity settings.',
547 VALUE_OPTIONAL),
3ec163dd
EL
548 'completionnotify' => new external_value(PARAM_INT,
549 '1: yes 0: no', VALUE_OPTIONAL),
550 'lang' => new external_value(PARAM_SAFEDIR,
551 'forced course language', VALUE_OPTIONAL),
552 'forcetheme' => new external_value(PARAM_PLUGIN,
553 'name of the force theme', VALUE_OPTIONAL),
8d8d4da4 554 'courseformatoptions' => new external_multiple_structure(
0e984d98 555 new external_single_structure(
8d8d4da4
MG
556 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
557 'value' => new external_value(PARAM_RAW, 'course format option value')
0e984d98
MG
558 )),
559 'additional options for particular course format', VALUE_OPTIONAL
560 ),
3ec163dd 561 ), 'course'
479a5db1 562 )
479a5db1
FS
563 );
564 }
565
6bb31e40 566 /**
567 * Returns description of method parameters
4615817d 568 *
6bb31e40 569 * @return external_function_parameters
4615817d 570 * @since Moodle 2.2
6bb31e40 571 */
572 public static function create_courses_parameters() {
573 $courseconfig = get_config('moodlecourse'); //needed for many default values
574 return new external_function_parameters(
575 array(
576 'courses' => new external_multiple_structure(
577 new external_single_structure(
578 array(
579 'fullname' => new external_value(PARAM_TEXT, 'full name'),
580 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
581 'categoryid' => new external_value(PARAM_INT, 'category id'),
582 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
583 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
93ce0e82 584 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
aff24313 585 'format' => new external_value(PARAM_PLUGIN,
6bb31e40 586 'course format: weeks, topics, social, site,..',
587 VALUE_DEFAULT, $courseconfig->format),
588 'showgrades' => new external_value(PARAM_INT,
589 '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
590 $courseconfig->showgrades),
591 'newsitems' => new external_value(PARAM_INT,
592 'number of recent items appearing on the course page',
593 VALUE_DEFAULT, $courseconfig->newsitems),
594 'startdate' => new external_value(PARAM_INT,
595 'timestamp when the course start', VALUE_OPTIONAL),
fbcdb0d7
DNA
596 'enddate' => new external_value(PARAM_INT,
597 'timestamp when the course end', VALUE_OPTIONAL),
0e984d98 598 'numsections' => new external_value(PARAM_INT,
8d8d4da4 599 '(deprecated, use courseformatoptions) number of weeks/topics',
0e984d98 600 VALUE_OPTIONAL),
6bb31e40 601 'maxbytes' => new external_value(PARAM_INT,
602 'largest size of file that can be uploaded into the course',
603 VALUE_DEFAULT, $courseconfig->maxbytes),
604 'showreports' => new external_value(PARAM_INT,
605 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
606 $courseconfig->showreports),
607 'visible' => new external_value(PARAM_INT,
608 '1: available to student, 0:not available', VALUE_OPTIONAL),
609 'hiddensections' => new external_value(PARAM_INT,
8d8d4da4 610 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
0e984d98 611 VALUE_OPTIONAL),
6bb31e40 612 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
613 VALUE_DEFAULT, $courseconfig->groupmode),
614 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
615 VALUE_DEFAULT, $courseconfig->groupmodeforce),
616 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
617 VALUE_DEFAULT, 0),
618 'enablecompletion' => new external_value(PARAM_INT,
8a6b1193 619 'Enabled, control via completion and activity settings. Disabled,
6bb31e40 620 not shown in activity settings.',
621 VALUE_OPTIONAL),
6bb31e40 622 'completionnotify' => new external_value(PARAM_INT,
623 '1: yes 0: no', VALUE_OPTIONAL),
aff24313 624 'lang' => new external_value(PARAM_SAFEDIR,
6bb31e40 625 'forced course language', VALUE_OPTIONAL),
aff24313 626 'forcetheme' => new external_value(PARAM_PLUGIN,
6bb31e40 627 'name of the force theme', VALUE_OPTIONAL),
8d8d4da4 628 'courseformatoptions' => new external_multiple_structure(
0e984d98 629 new external_single_structure(
8d8d4da4
MG
630 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
631 'value' => new external_value(PARAM_RAW, 'course format option value')
0e984d98
MG
632 )),
633 'additional options for particular course format', VALUE_OPTIONAL),
6bb31e40 634 )
635 ), 'courses to create'
636 )
637 )
638 );
639 }
640
641 /**
642 * Create courses
4615817d 643 *
6bb31e40 644 * @param array $courses
645 * @return array courses (id and shortname only)
4615817d 646 * @since Moodle 2.2
6bb31e40 647 */
648 public static function create_courses($courses) {
649 global $CFG, $DB;
650 require_once($CFG->dirroot . "/course/lib.php");
651 require_once($CFG->libdir . '/completionlib.php');
652
6bb31e40 653 $params = self::validate_parameters(self::create_courses_parameters(),
654 array('courses' => $courses));
655
bd3b3bba 656 $availablethemes = core_component::get_plugin_list('theme');
6bb31e40 657 $availablelangs = get_string_manager()->get_list_of_translations();
658
659 $transaction = $DB->start_delegated_transaction();
660
661 foreach ($params['courses'] as $course) {
662
663 // Ensure the current user is allowed to run this function
1f364c87 664 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
6bb31e40 665 try {
666 self::validate_context($context);
667 } catch (Exception $e) {
668 $exceptionparam = new stdClass();
669 $exceptionparam->message = $e->getMessage();
670 $exceptionparam->catid = $course['categoryid'];
96d3b93b 671 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
6bb31e40 672 }
673 require_capability('moodle/course:create', $context);
674
675 // Make sure lang is valid
12fc8acf 676 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
96d3b93b 677 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
6bb31e40 678 }
679
680 // Make sure theme is valid
12fc8acf 681 if (array_key_exists('forcetheme', $course)) {
6bb31e40 682 if (!empty($CFG->allowcoursethemes)) {
683 if (empty($availablethemes[$course['forcetheme']])) {
96d3b93b 684 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
6bb31e40 685 } else {
686 $course['theme'] = $course['forcetheme'];
687 }
688 }
689 }
690
691 //force visibility if ws user doesn't have the permission to set it
692 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
693 if (!has_capability('moodle/course:visibility', $context)) {
694 $course['visible'] = $category->visible;
695 }
696
697 //set default value for completion
8a6b1193 698 $courseconfig = get_config('moodlecourse');
6bb31e40 699 if (completion_info::is_enabled_for_site()) {
12fc8acf 700 if (!array_key_exists('enablecompletion', $course)) {
8a6b1193 701 $course['enablecompletion'] = $courseconfig->enablecompletion;
6bb31e40 702 }
6bb31e40 703 } else {
704 $course['enablecompletion'] = 0;
6bb31e40 705 }
706
707 $course['category'] = $course['categoryid'];
708
93ce0e82
JM
709 // Summary format.
710 $course['summaryformat'] = external_validate_format($course['summaryformat']);
711
8d8d4da4
MG
712 if (!empty($course['courseformatoptions'])) {
713 foreach ($course['courseformatoptions'] as $option) {
714 $course[$option['name']] = $option['value'];
0e984d98
MG
715 }
716 }
717
6bb31e40 718 //Note: create_course() core function check shortname, idnumber, category
719 $course['id'] = create_course((object) $course)->id;
720
721 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
722 }
723
724 $transaction->allow_commit();
725
726 return $resultcourses;
727 }
728
729 /**
730 * Returns description of method result value
4615817d 731 *
6bb31e40 732 * @return external_description
4615817d 733 * @since Moodle 2.2
6bb31e40 734 */
735 public static function create_courses_returns() {
736 return new external_multiple_structure(
737 new external_single_structure(
738 array(
739 'id' => new external_value(PARAM_INT, 'course id'),
740 'shortname' => new external_value(PARAM_TEXT, 'short name'),
741 )
742 )
743 );
744 }
745
791723c3
RT
746 /**
747 * Update courses
748 *
749 * @return external_function_parameters
750 * @since Moodle 2.5
751 */
752 public static function update_courses_parameters() {
753 return new external_function_parameters(
754 array(
755 'courses' => new external_multiple_structure(
756 new external_single_structure(
757 array(
758 'id' => new external_value(PARAM_INT, 'ID of the course'),
759 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL),
760 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL),
761 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL),
762 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
763 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
764 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
765 'format' => new external_value(PARAM_PLUGIN,
766 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
767 'showgrades' => new external_value(PARAM_INT,
768 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
769 'newsitems' => new external_value(PARAM_INT,
770 'number of recent items appearing on the course page', VALUE_OPTIONAL),
771 'startdate' => new external_value(PARAM_INT,
772 'timestamp when the course start', VALUE_OPTIONAL),
fbcdb0d7
DNA
773 'enddate' => new external_value(PARAM_INT,
774 'timestamp when the course end', VALUE_OPTIONAL),
791723c3
RT
775 'numsections' => new external_value(PARAM_INT,
776 '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL),
777 'maxbytes' => new external_value(PARAM_INT,
778 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL),
779 'showreports' => new external_value(PARAM_INT,
780 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
781 'visible' => new external_value(PARAM_INT,
782 '1: available to student, 0:not available', VALUE_OPTIONAL),
783 'hiddensections' => new external_value(PARAM_INT,
e2adaaf7 784 '(deprecated, use courseformatoptions) How the hidden sections in the course are
791723c3
RT
785 displayed to students', VALUE_OPTIONAL),
786 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
787 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
788 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
789 'enablecompletion' => new external_value(PARAM_INT,
790 'Enabled, control via completion and activity settings. Disabled,
791 not shown in activity settings.', VALUE_OPTIONAL),
791723c3
RT
792 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
793 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL),
794 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL),
795 'courseformatoptions' => new external_multiple_structure(
796 new external_single_structure(
797 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
798 'value' => new external_value(PARAM_RAW, 'course format option value')
799 )),
800 'additional options for particular course format', VALUE_OPTIONAL),
801 )
802 ), 'courses to update'
803 )
804 )
805 );
806 }
807
808 /**
809 * Update courses
810 *
811 * @param array $courses
812 * @since Moodle 2.5
813 */
814 public static function update_courses($courses) {
815 global $CFG, $DB;
816 require_once($CFG->dirroot . "/course/lib.php");
817 $warnings = array();
818
819 $params = self::validate_parameters(self::update_courses_parameters(),
820 array('courses' => $courses));
821
bd3b3bba 822 $availablethemes = core_component::get_plugin_list('theme');
791723c3
RT
823 $availablelangs = get_string_manager()->get_list_of_translations();
824
825 foreach ($params['courses'] as $course) {
826 // Catch any exception while updating course and return as warning to user.
827 try {
828 // Ensure the current user is allowed to run this function.
829 $context = context_course::instance($course['id'], MUST_EXIST);
830 self::validate_context($context);
831
832 $oldcourse = course_get_format($course['id'])->get_course();
833
834 require_capability('moodle/course:update', $context);
835
836 // Check if user can change category.
837 if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) {
838 require_capability('moodle/course:changecategory', $context);
839 $course['category'] = $course['categoryid'];
840 }
841
842 // Check if the user can change fullname.
843 if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) {
844 require_capability('moodle/course:changefullname', $context);
845 }
846
5536a561 847 // Check if the user can change shortname.
791723c3
RT
848 if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
849 require_capability('moodle/course:changeshortname', $context);
791723c3
RT
850 }
851
5536a561 852 // Check if the user can change the idnumber.
791723c3
RT
853 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
854 require_capability('moodle/course:changeidnumber', $context);
791723c3
RT
855 }
856
857 // Check if user can change summary.
858 if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) {
859 require_capability('moodle/course:changesummary', $context);
860 }
861
862 // Summary format.
863 if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) {
864 require_capability('moodle/course:changesummary', $context);
865 $course['summaryformat'] = external_validate_format($course['summaryformat']);
866 }
867
868 // Check if user can change visibility.
869 if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) {
870 require_capability('moodle/course:visibility', $context);
871 }
872
873 // Make sure lang is valid.
874 if (array_key_exists('lang', $course) && empty($availablelangs[$course['lang']])) {
875 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
876 }
877
878 // Make sure theme is valid.
879 if (array_key_exists('forcetheme', $course)) {
880 if (!empty($CFG->allowcoursethemes)) {
881 if (empty($availablethemes[$course['forcetheme']])) {
882 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
883 } else {
884 $course['theme'] = $course['forcetheme'];
885 }
886 }
887 }
888
889 // Make sure completion is enabled before setting it.
8819a836 890 if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) {
791723c3 891 $course['enabledcompletion'] = 0;
791723c3
RT
892 }
893
894 // Make sure maxbytes are less then CFG->maxbytes.
895 if (array_key_exists('maxbytes', $course)) {
896 $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
897 }
898
899 if (!empty($course['courseformatoptions'])) {
900 foreach ($course['courseformatoptions'] as $option) {
901 if (isset($option['name']) && isset($option['value'])) {
902 $course[$option['name']] = $option['value'];
903 }
904 }
905 }
906
907 // Update course if user has all required capabilities.
908 update_course((object) $course);
909 } catch (Exception $e) {
910 $warning = array();
911 $warning['item'] = 'course';
912 $warning['itemid'] = $course['id'];
913 if ($e instanceof moodle_exception) {
914 $warning['warningcode'] = $e->errorcode;
915 } else {
916 $warning['warningcode'] = $e->getCode();
917 }
918 $warning['message'] = $e->getMessage();
919 $warnings[] = $warning;
920 }
921 }
922
923 $result = array();
924 $result['warnings'] = $warnings;
925 return $result;
926 }
927
928 /**
929 * Returns description of method result value
930 *
931 * @return external_description
932 * @since Moodle 2.5
933 */
934 public static function update_courses_returns() {
935 return new external_single_structure(
936 array(
937 'warnings' => new external_warnings()
938 )
939 );
940 }
941
63a85dc7
JL
942 /**
943 * Returns description of method parameters
3ec163dd 944 *
63a85dc7 945 * @return external_function_parameters
3ec163dd 946 * @since Moodle 2.2
63a85dc7
JL
947 */
948 public static function delete_courses_parameters() {
949 return new external_function_parameters(
950 array(
951 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
952 )
953 );
954 }
955
956 /**
957 * Delete courses
3ec163dd 958 *
63a85dc7 959 * @param array $courseids A list of course ids
3ec163dd 960 * @since Moodle 2.2
63a85dc7
JL
961 */
962 public static function delete_courses($courseids) {
963 global $CFG, $DB;
964 require_once($CFG->dirroot."/course/lib.php");
965
966 // Parameter validation.
967 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
968
70f37963 969 $warnings = array();
63a85dc7
JL
970
971 foreach ($params['courseids'] as $courseid) {
70f37963
JH
972 $course = $DB->get_record('course', array('id' => $courseid));
973
974 if ($course === false) {
975 $warnings[] = array(
976 'item' => 'course',
977 'itemid' => $courseid,
978 'warningcode' => 'unknowncourseidnumber',
979 'message' => 'Unknown course ID ' . $courseid
980 );
981 continue;
982 }
63a85dc7
JL
983
984 // Check if the context is valid.
985 $coursecontext = context_course::instance($course->id);
986 self::validate_context($coursecontext);
987
70f37963 988 // Check if the current user has permission.
63a85dc7 989 if (!can_delete_course($courseid)) {
70f37963
JH
990 $warnings[] = array(
991 'item' => 'course',
992 'itemid' => $courseid,
993 'warningcode' => 'cannotdeletecourse',
994 'message' => 'You do not have the permission to delete this course' . $courseid
995 );
996 continue;
63a85dc7
JL
997 }
998
70f37963
JH
999 if (delete_course($course, false) === false) {
1000 $warnings[] = array(
1001 'item' => 'course',
1002 'itemid' => $courseid,
1003 'warningcode' => 'cannotdeletecategorycourse',
1004 'message' => 'Course ' . $courseid . ' failed to be deleted'
1005 );
1006 continue;
1007 }
63a85dc7
JL
1008 }
1009
70f37963 1010 fix_course_sortorder();
63a85dc7 1011
70f37963 1012 return array('warnings' => $warnings);
63a85dc7
JL
1013 }
1014
1015 /**
1016 * Returns description of method result value
3ec163dd 1017 *
63a85dc7 1018 * @return external_description
3ec163dd 1019 * @since Moodle 2.2
63a85dc7
JL
1020 */
1021 public static function delete_courses_returns() {
70f37963
JH
1022 return new external_single_structure(
1023 array(
1024 'warnings' => new external_warnings()
1025 )
1026 );
63a85dc7
JL
1027 }
1028
3dc1d76e
JL
1029 /**
1030 * Returns description of method parameters
1031 *
1032 * @return external_function_parameters
1033 * @since Moodle 2.3
1034 */
1035 public static function duplicate_course_parameters() {
1036 return new external_function_parameters(
1037 array(
1038 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
1039 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
1040 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
1041 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
1042 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
1043 'options' => new external_multiple_structure(
1044 new external_single_structure(
1045 array(
3dfc29e1 1046 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
9aa84e91
JL
1047 "activities" (int) Include course activites (default to 1 that is equal to yes),
1048 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1049 "filters" (int) Include course filters (default to 1 that is equal to yes),
1050 "users" (int) Include users (default to 0 that is equal to no),
1051 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
9aa84e91 1052 "comments" (int) Include user comments (default to 0 that is equal to no),
7469c512 1053 "userscompletion" (int) Include user course completion information (default to 0 that is equal to no),
9aa84e91 1054 "logs" (int) Include course logs (default to 0 that is equal to no),
7469c512 1055 "grade_histories" (int) Include histories (default to 0 that is equal to no)'
9aa84e91
JL
1056 ),
1057 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1058 )
3dc1d76e
JL
1059 )
1060 ), VALUE_DEFAULT, array()
1061 ),
1062 )
1063 );
1064 }
1065
1066 /**
1067 * Duplicate a course
1068 *
1069 * @param int $courseid
1070 * @param string $fullname Duplicated course fullname
1071 * @param string $shortname Duplicated course shortname
1072 * @param int $categoryid Duplicated course parent category id
1073 * @param int $visible Duplicated course availability
1074 * @param array $options List of backup options
1075 * @return array New course info
1076 * @since Moodle 2.3
1077 */
3297d575 1078 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
3dc1d76e
JL
1079 global $CFG, $USER, $DB;
1080 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1081 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1082
1083 // Parameter validation.
1084 $params = self::validate_parameters(
1085 self::duplicate_course_parameters(),
1086 array(
1087 'courseid' => $courseid,
1088 'fullname' => $fullname,
1089 'shortname' => $shortname,
1090 'categoryid' => $categoryid,
1091 'visible' => $visible,
1092 'options' => $options
1093 )
1094 );
1095
3ec163dd
EL
1096 // Context validation.
1097
1098 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
19a86468 1099 throw new moodle_exception('invalidcourseid', 'error');
3ec163dd
EL
1100 }
1101
1102 // Category where duplicated course is going to be created.
1103 $categorycontext = context_coursecat::instance($params['categoryid']);
1104 self::validate_context($categorycontext);
1105
1106 // Course to be duplicated.
1107 $coursecontext = context_course::instance($course->id);
1108 self::validate_context($coursecontext);
1109
1110 $backupdefaults = array(
1111 'activities' => 1,
1112 'blocks' => 1,
1113 'filters' => 1,
1114 'users' => 0,
1115 'role_assignments' => 0,
3ec163dd 1116 'comments' => 0,
7469c512 1117 'userscompletion' => 0,
3ec163dd 1118 'logs' => 0,
7469c512 1119 'grade_histories' => 0
3ec163dd
EL
1120 );
1121
1122 $backupsettings = array();
1123 // Check for backup and restore options.
1124 if (!empty($params['options'])) {
1125 foreach ($params['options'] as $option) {
1126
1127 // Strict check for a correct value (allways 1 or 0, true or false).
1128 $value = clean_param($option['value'], PARAM_INT);
1129
1130 if ($value !== 0 and $value !== 1) {
1131 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1132 }
1133
1134 if (!isset($backupdefaults[$option['name']])) {
1135 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1136 }
1137
1138 $backupsettings[$option['name']] = $value;
1139 }
1140 }
1141
1142 // Capability checking.
1143
1144 // The backup controller check for this currently, this may be redundant.
1145 require_capability('moodle/course:create', $categorycontext);
1146 require_capability('moodle/restore:restorecourse', $categorycontext);
1147 require_capability('moodle/backup:backupcourse', $coursecontext);
1148
1149 if (!empty($backupsettings['users'])) {
1150 require_capability('moodle/backup:userinfo', $coursecontext);
1151 require_capability('moodle/restore:userinfo', $categorycontext);
1152 }
1153
1154 // Check if the shortname is used.
1155 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
1156 foreach ($foundcourses as $foundcourse) {
1157 $foundcoursenames[] = $foundcourse->fullname;
1158 }
1159
1160 $foundcoursenamestring = implode(',', $foundcoursenames);
1161 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1162 }
1163
1164 // Backup the course.
1165
1166 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1167 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
1168
1169 foreach ($backupsettings as $name => $value) {
1170 $bc->get_plan()->get_setting($name)->set_value($value);
1171 }
1172
1173 $backupid = $bc->get_backupid();
1174 $backupbasepath = $bc->get_plan()->get_basepath();
1175
1176 $bc->execute_plan();
1177 $results = $bc->get_results();
1178 $file = $results['backup_destination'];
1179
1180 $bc->destroy();
1181
1182 // Restore the backup immediately.
1183
1184 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
1185 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
00219425 1186 $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath);
3ec163dd
EL
1187 }
1188
1189 // Create new course.
1190 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1191
1192 $rc = new restore_controller($backupid, $newcourseid,
1193 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
1194
1195 foreach ($backupsettings as $name => $value) {
1196 $setting = $rc->get_plan()->get_setting($name);
1197 if ($setting->get_status() == backup_setting::NOT_LOCKED) {
1198 $setting->set_value($value);
1199 }
1200 }
1201
1202 if (!$rc->execute_precheck()) {
1203 $precheckresults = $rc->get_precheck_results();
1204 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1205 if (empty($CFG->keeptempdirectoriesonbackup)) {
1206 fulldelete($backupbasepath);
1207 }
1208
1209 $errorinfo = '';
1210
1211 foreach ($precheckresults['errors'] as $error) {
1212 $errorinfo .= $error;
1213 }
1214
1215 if (array_key_exists('warnings', $precheckresults)) {
1216 foreach ($precheckresults['warnings'] as $warning) {
1217 $errorinfo .= $warning;
1218 }
1219 }
1220
1221 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1222 }
1223 }
1224
1225 $rc->execute_plan();
1226 $rc->destroy();
1227
1228 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
1229 $course->fullname = $params['fullname'];
1230 $course->shortname = $params['shortname'];
1231 $course->visible = $params['visible'];
1232
1233 // Set shortname and fullname back.
1234 $DB->update_record('course', $course);
1235
1236 if (empty($CFG->keeptempdirectoriesonbackup)) {
1237 fulldelete($backupbasepath);
1238 }
1239
1240 // Delete the course backup file created by this WebService. Originally located in the course backups area.
1241 $file->delete();
1242
1243 return array('id' => $course->id, 'shortname' => $course->shortname);
1244 }
1245
1246 /**
1247 * Returns description of method result value
1248 *
1249 * @return external_description
1250 * @since Moodle 2.3
1251 */
1252 public static function duplicate_course_returns() {
1253 return new external_single_structure(
1254 array(
1255 'id' => new external_value(PARAM_INT, 'course id'),
1256 'shortname' => new external_value(PARAM_TEXT, 'short name'),
1257 )
1258 );
1259 }
1260
8430d87b 1261 /**
c1483c9c 1262 * Returns description of method parameters for import_course
8430d87b
JL
1263 *
1264 * @return external_function_parameters
c1483c9c 1265 * @since Moodle 2.4
8430d87b
JL
1266 */
1267 public static function import_course_parameters() {
1268 return new external_function_parameters(
1269 array(
1270 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
1271 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
1272 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
1273 'options' => new external_multiple_structure(
1274 new external_single_structure(
1275 array(
1276 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
1277 "activities" (int) Include course activites (default to 1 that is equal to yes),
1278 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1279 "filters" (int) Include course filters (default to 1 that is equal to yes)'
1280 ),
1281 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1282 )
1283 )
1284 ), VALUE_DEFAULT, array()
1285 ),
1286 )
1287 );
1288 }
1289
1290 /**
1291 * Imports a course
1292 *
1293 * @param int $importfrom The id of the course we are importing from
1294 * @param int $importto The id of the course we are importing to
1295 * @param bool $deletecontent Whether to delete the course we are importing to content
1296 * @param array $options List of backup options
1297 * @return null
c1483c9c 1298 * @since Moodle 2.4
8430d87b 1299 */
b5bd42e8 1300 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
8430d87b
JL
1301 global $CFG, $USER, $DB;
1302 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1303 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1304
1305 // Parameter validation.
1306 $params = self::validate_parameters(
c1483c9c
SH
1307 self::import_course_parameters(),
1308 array(
1309 'importfrom' => $importfrom,
1310 'importto' => $importto,
1311 'deletecontent' => $deletecontent,
1312 'options' => $options
1313 )
8430d87b
JL
1314 );
1315
1316 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1b2f5493 1317 throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
8430d87b
JL
1318 }
1319
1320 // Context validation.
1321
1322 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
0b9a3d7a 1323 throw new moodle_exception('invalidcourseid', 'error');
8430d87b
JL
1324 }
1325
1326 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
0b9a3d7a 1327 throw new moodle_exception('invalidcourseid', 'error');
8430d87b
JL
1328 }
1329
1330 $importfromcontext = context_course::instance($importfrom->id);
1331 self::validate_context($importfromcontext);
1332
1333 $importtocontext = context_course::instance($importto->id);
1334 self::validate_context($importtocontext);
1335
1336 $backupdefaults = array(
c1483c9c
SH
1337 'activities' => 1,
1338 'blocks' => 1,
1339 'filters' => 1
8430d87b
JL
1340 );
1341
1342 $backupsettings = array();
1343
1344 // Check for backup and restore options.
1345 if (!empty($params['options'])) {
1346 foreach ($params['options'] as $option) {
1347
1348 // Strict check for a correct value (allways 1 or 0, true or false).
1349 $value = clean_param($option['value'], PARAM_INT);
1350
1351 if ($value !== 0 and $value !== 1) {
1352 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1353 }
1354
1355 if (!isset($backupdefaults[$option['name']])) {
1356 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1357 }
1358
1359 $backupsettings[$option['name']] = $value;
1360 }
1361 }
1362
1363 // Capability checking.
1364
1365 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1366 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1367
1368 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1369 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1370
1371 foreach ($backupsettings as $name => $value) {
1372 $bc->get_plan()->get_setting($name)->set_value($value);
1373 }
1374
1375 $backupid = $bc->get_backupid();
1376 $backupbasepath = $bc->get_plan()->get_basepath();
1377
1378 $bc->execute_plan();
1379 $bc->destroy();
1380
1381 // Restore the backup immediately.
1382
1383 // Check if we must delete the contents of the destination course.
1384 if ($params['deletecontent']) {
1385 $restoretarget = backup::TARGET_EXISTING_DELETING;
1386 } else {
1387 $restoretarget = backup::TARGET_EXISTING_ADDING;
1388 }
1389
1390 $rc = new restore_controller($backupid, $importto->id,
1391 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1392
1393 foreach ($backupsettings as $name => $value) {
1394 $rc->get_plan()->get_setting($name)->set_value($value);
1395 }
1396
1397 if (!$rc->execute_precheck()) {
1398 $precheckresults = $rc->get_precheck_results();
1399 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1400 if (empty($CFG->keeptempdirectoriesonbackup)) {
1401 fulldelete($backupbasepath);
1402 }
1403
1404 $errorinfo = '';
1405
1406 foreach ($precheckresults['errors'] as $error) {
1407 $errorinfo .= $error;
1408 }
1409
1410 if (array_key_exists('warnings', $precheckresults)) {
1411 foreach ($precheckresults['warnings'] as $warning) {
1412 $errorinfo .= $warning;
1413 }
1414 }
1415
1416 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1417 }
1418 } else {
1419 if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1420 restore_dbops::delete_course_content($importto->id);
1421 }
1422 }
1423
1424 $rc->execute_plan();
1425 $rc->destroy();
1426
1427 if (empty($CFG->keeptempdirectoriesonbackup)) {
1428 fulldelete($backupbasepath);
1429 }
1430
1431 return null;
1432 }
1433
1434 /**
1435 * Returns description of method result value
1436 *
1437 * @return external_description
c1483c9c 1438 * @since Moodle 2.4
8430d87b
JL
1439 */
1440 public static function import_course_returns() {
1441 return null;
1442 }
1443
3ec163dd
EL
1444 /**
1445 * Returns description of method parameters
1446 *
1447 * @return external_function_parameters
1448 * @since Moodle 2.3
1449 */
1450 public static function get_categories_parameters() {
1451 return new external_function_parameters(
1452 array(
1453 'criteria' => new external_multiple_structure(
1454 new external_single_structure(
1455 array(
1456 'key' => new external_value(PARAM_ALPHA,
1457 'The category column to search, expected keys (value format) are:'.
1458 '"id" (int) the category id,'.
c1da311a 1459 '"ids" (string) category ids separated by commas,'.
3ec163dd
EL
1460 '"name" (string) the category name,'.
1461 '"parent" (int) the parent category id,'.
1462 '"idnumber" (string) category idnumber'.
1463 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
e6d1218a
JM
1464 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1465 then the function return all categories that the user can see.'.
3ec163dd 1466 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
e6d1218a 1467 '"theme" (string) only return the categories having this theme'.
3ec163dd
EL
1468 ' - user must have \'moodle/category:manage\' to search on theme'),
1469 'value' => new external_value(PARAM_RAW, 'the value to match')
1470 )
7a384506 1471 ), 'criteria', VALUE_DEFAULT, array()
3ec163dd
EL
1472 ),
1473 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1474 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1475 )
1476 );
1477 }
1478
1479 /**
1480 * Get categories
1481 *
1482 * @param array $criteria Criteria to match the results
1483 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1484 * @return array list of categories
1485 * @since Moodle 2.3
1486 */
1487 public static function get_categories($criteria = array(), $addsubcategories = true) {
1488 global $CFG, $DB;
1489 require_once($CFG->dirroot . "/course/lib.php");
1490
1491 // Validate parameters.
1492 $params = self::validate_parameters(self::get_categories_parameters(),
1493 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1494
1495 // Retrieve the categories.
1496 $categories = array();
1497 if (!empty($params['criteria'])) {
1498
1499 $conditions = array();
1500 $wheres = array();
1501 foreach ($params['criteria'] as $crit) {
1502 $key = trim($crit['key']);
1503
1504 // Trying to avoid duplicate keys.
1505 if (!isset($conditions[$key])) {
3dc1d76e 1506
3ec163dd
EL
1507 $context = context_system::instance();
1508 $value = null;
1509 switch ($key) {
1510 case 'id':
1511 $value = clean_param($crit['value'], PARAM_INT);
c1da311a
JL
1512 $conditions[$key] = $value;
1513 $wheres[] = $key . " = :" . $key;
1514 break;
1515
1516 case 'ids':
1517 $value = clean_param($crit['value'], PARAM_SEQUENCE);
1518 $ids = explode(',', $value);
1519 list($sqlids, $paramids) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
1520 $conditions = array_merge($conditions, $paramids);
1521 $wheres[] = 'id ' . $sqlids;
3ec163dd 1522 break;
3dc1d76e 1523
3ec163dd
EL
1524 case 'idnumber':
1525 if (has_capability('moodle/category:manage', $context)) {
1526 $value = clean_param($crit['value'], PARAM_RAW);
c1da311a
JL
1527 $conditions[$key] = $value;
1528 $wheres[] = $key . " = :" . $key;
3ec163dd
EL
1529 } else {
1530 // We must throw an exception.
1531 // Otherwise the dev client would think no idnumber exists.
1532 throw new moodle_exception('criteriaerror',
1533 'webservice', '', null,
1534 'You don\'t have the permissions to search on the "idnumber" field.');
1535 }
1536 break;
3dc1d76e 1537
3ec163dd
EL
1538 case 'name':
1539 $value = clean_param($crit['value'], PARAM_TEXT);
c1da311a
JL
1540 $conditions[$key] = $value;
1541 $wheres[] = $key . " = :" . $key;
3ec163dd 1542 break;
3dc1d76e 1543
3ec163dd
EL
1544 case 'parent':
1545 $value = clean_param($crit['value'], PARAM_INT);
c1da311a
JL
1546 $conditions[$key] = $value;
1547 $wheres[] = $key . " = :" . $key;
3ec163dd 1548 break;
9aa84e91 1549
3ec163dd
EL
1550 case 'visible':
1551 if (has_capability('moodle/category:manage', $context)
1552 or has_capability('moodle/category:viewhiddencategories',
1553 context_system::instance())) {
1554 $value = clean_param($crit['value'], PARAM_INT);
c1da311a
JL
1555 $conditions[$key] = $value;
1556 $wheres[] = $key . " = :" . $key;
3ec163dd
EL
1557 } else {
1558 throw new moodle_exception('criteriaerror',
1559 'webservice', '', null,
1560 'You don\'t have the permissions to search on the "visible" field.');
1561 }
1562 break;
9aa84e91 1563
3ec163dd
EL
1564 case 'theme':
1565 if (has_capability('moodle/category:manage', $context)) {
1566 $value = clean_param($crit['value'], PARAM_THEME);
c1da311a
JL
1567 $conditions[$key] = $value;
1568 $wheres[] = $key . " = :" . $key;
3ec163dd
EL
1569 } else {
1570 throw new moodle_exception('criteriaerror',
1571 'webservice', '', null,
1572 'You don\'t have the permissions to search on the "theme" field.');
1573 }
1574 break;
9aa84e91 1575
3ec163dd
EL
1576 default:
1577 throw new moodle_exception('criteriaerror',
1578 'webservice', '', null,
1579 'You can not search on this criteria: ' . $key);
1580 }
9aa84e91 1581 }
9aa84e91 1582 }
9aa84e91 1583
3ec163dd
EL
1584 if (!empty($wheres)) {
1585 $wheres = implode(" AND ", $wheres);
3dc1d76e 1586
3ec163dd 1587 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
3dc1d76e 1588
3ec163dd
EL
1589 // Retrieve its sub subcategories (all levels).
1590 if ($categories and !empty($params['addsubcategories'])) {
1591 $newcategories = array();
9aa84e91 1592
e6d1218a
JM
1593 // Check if we required visible/theme checks.
1594 $additionalselect = '';
1595 $additionalparams = array();
1596 if (isset($conditions['visible'])) {
1597 $additionalselect .= ' AND visible = :visible';
1598 $additionalparams['visible'] = $conditions['visible'];
1599 }
1600 if (isset($conditions['theme'])) {
1601 $additionalselect .= ' AND theme= :theme';
1602 $additionalparams['theme'] = $conditions['theme'];
1603 }
1604
3ec163dd 1605 foreach ($categories as $category) {
e6d1218a
JM
1606 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1607 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1608 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
3ec163dd
EL
1609 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1610 }
1611 $categories = $categories + $newcategories;
1612 }
3dc1d76e
JL
1613 }
1614
3ec163dd
EL
1615 } else {
1616 // Retrieve all categories in the database.
1617 $categories = $DB->get_records('course_categories');
3dc1d76e
JL
1618 }
1619
3ec163dd
EL
1620 // The not returned categories. key => category id, value => reason of exclusion.
1621 $excludedcats = array();
3dc1d76e 1622
3ec163dd
EL
1623 // The returned categories.
1624 $categoriesinfo = array();
6c7d3e31 1625
3ec163dd
EL
1626 // We need to sort the categories by path.
1627 // The parent cats need to be checked by the algo first.
1628 usort($categories, "core_course_external::compare_categories_by_path");
3dc1d76e 1629
3ec163dd 1630 foreach ($categories as $category) {
3dc1d76e 1631
3ec163dd
EL
1632 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1633 $parents = explode('/', $category->path);
1634 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1635 foreach ($parents as $parentid) {
1636 // Note: when the parent exclusion was due to the context,
1637 // the sub category could still be returned.
1638 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1639 $excludedcats[$category->id] = 'parent';
1640 }
1641 }
9aa84e91 1642
3ec163dd
EL
1643 // Check category depth is <= maxdepth (do not check for user who can manage categories).
1644 if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
1645 and !has_capability('moodle/category:manage', $context)) {
1646 $excludedcats[$category->id] = 'depth';
1647 }
3dc1d76e 1648
3ec163dd
EL
1649 // Check the user can use the category context.
1650 $context = context_coursecat::instance($category->id);
1651 try {
1652 self::validate_context($context);
1653 } catch (Exception $e) {
1654 $excludedcats[$category->id] = 'context';
3dc1d76e 1655
3ec163dd
EL
1656 // If it was the requested category then throw an exception.
1657 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1658 $exceptionparam = new stdClass();
1659 $exceptionparam->message = $e->getMessage();
1660 $exceptionparam->catid = $category->id;
1661 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1662 }
9aa84e91 1663 }
3dc1d76e 1664
3ec163dd
EL
1665 // Return the category information.
1666 if (!isset($excludedcats[$category->id])) {
3dc1d76e 1667
3ec163dd
EL
1668 // Final check to see if the category is visible to the user.
1669 if ($category->visible
1670 or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1671 or has_capability('moodle/category:manage', $context)) {
3dc1d76e 1672
3ec163dd
EL
1673 $categoryinfo = array();
1674 $categoryinfo['id'] = $category->id;
1675 $categoryinfo['name'] = $category->name;
93ce0e82
JM
1676 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1677 external_format_text($category->description, $category->descriptionformat,
1678 $context->id, 'coursecat', 'description', null);
3ec163dd
EL
1679 $categoryinfo['parent'] = $category->parent;
1680 $categoryinfo['sortorder'] = $category->sortorder;
1681 $categoryinfo['coursecount'] = $category->coursecount;
1682 $categoryinfo['depth'] = $category->depth;
1683 $categoryinfo['path'] = $category->path;
3dc1d76e 1684
3ec163dd
EL
1685 // Some fields only returned for admin.
1686 if (has_capability('moodle/category:manage', $context)) {
1687 $categoryinfo['idnumber'] = $category->idnumber;
1688 $categoryinfo['visible'] = $category->visible;
1689 $categoryinfo['visibleold'] = $category->visibleold;
1690 $categoryinfo['timemodified'] = $category->timemodified;
1691 $categoryinfo['theme'] = $category->theme;
3dc1d76e 1692 }
3dc1d76e 1693
3ec163dd
EL
1694 $categoriesinfo[] = $categoryinfo;
1695 } else {
1696 $excludedcats[$category->id] = 'visibility';
1697 }
3dc1d76e
JL
1698 }
1699 }
1700
3ec163dd
EL
1701 // Sorting the resulting array so it looks a bit better for the client developer.
1702 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
3dc1d76e 1703
3ec163dd
EL
1704 return $categoriesinfo;
1705 }
3dc1d76e 1706
3ec163dd
EL
1707 /**
1708 * Sort categories array by path
1709 * private function: only used by get_categories
1710 *
1711 * @param array $category1
1712 * @param array $category2
1713 * @return int result of strcmp
1714 * @since Moodle 2.3
1715 */
1716 private static function compare_categories_by_path($category1, $category2) {
1717 return strcmp($category1->path, $category2->path);
1718 }
6c7d3e31 1719
3ec163dd
EL
1720 /**
1721 * Sort categories array by sortorder
1722 * private function: only used by get_categories
1723 *
1724 * @param array $category1
1725 * @param array $category2
1726 * @return int result of strcmp
1727 * @since Moodle 2.3
1728 */
1729 private static function compare_categories_by_sortorder($category1, $category2) {
1730 return strcmp($category1['sortorder'], $category2['sortorder']);
3dc1d76e
JL
1731 }
1732
1733 /**
1734 * Returns description of method result value
1735 *
1736 * @return external_description
1737 * @since Moodle 2.3
1738 */
3ec163dd
EL
1739 public static function get_categories_returns() {
1740 return new external_multiple_structure(
1741 new external_single_structure(
1742 array(
1743 'id' => new external_value(PARAM_INT, 'category id'),
1744 'name' => new external_value(PARAM_TEXT, 'category name'),
1745 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1746 'description' => new external_value(PARAM_RAW, 'category description'),
93ce0e82 1747 'descriptionformat' => new external_format_value('description'),
3ec163dd
EL
1748 'parent' => new external_value(PARAM_INT, 'parent category id'),
1749 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1750 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1751 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1752 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1753 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1754 'depth' => new external_value(PARAM_INT, 'category depth'),
1755 'path' => new external_value(PARAM_TEXT, 'category path'),
1756 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1757 ), 'List of categories'
3dc1d76e
JL
1758 )
1759 );
1760 }
1761
2f951d86
FS
1762 /**
1763 * Returns description of method parameters
3ec163dd 1764 *
2f951d86
FS
1765 * @return external_function_parameters
1766 * @since Moodle 2.3
1767 */
3ec163dd 1768 public static function create_categories_parameters() {
2f951d86
FS
1769 return new external_function_parameters(
1770 array(
1771 'categories' => new external_multiple_structure(
3ec163dd
EL
1772 new external_single_structure(
1773 array(
1774 'name' => new external_value(PARAM_TEXT, 'new category name'),
1775 'parent' => new external_value(PARAM_INT,
9615b623
JM
1776 'the parent category id inside which the new category will be created
1777 - set to 0 for a root category',
1778 VALUE_DEFAULT, 0),
3ec163dd
EL
1779 'idnumber' => new external_value(PARAM_RAW,
1780 'the new category idnumber', VALUE_OPTIONAL),
1781 'description' => new external_value(PARAM_RAW,
1782 'the new category description', VALUE_OPTIONAL),
93ce0e82 1783 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
3ec163dd
EL
1784 'theme' => new external_value(PARAM_THEME,
1785 'the new category theme. This option must be enabled on moodle',
1786 VALUE_OPTIONAL),
2f951d86
FS
1787 )
1788 )
1789 )
1790 )
1791 );
1792 }
1793
1794 /**
3ec163dd
EL
1795 * Create categories
1796 *
1797 * @param array $categories - see create_categories_parameters() for the array structure
1798 * @return array - see create_categories_returns() for the array structure
2f951d86
FS
1799 * @since Moodle 2.3
1800 */
3ec163dd 1801 public static function create_categories($categories) {
2f951d86 1802 global $CFG, $DB;
9bad61db 1803 require_once($CFG->libdir . "/coursecatlib.php");
2f951d86 1804
3ec163dd
EL
1805 $params = self::validate_parameters(self::create_categories_parameters(),
1806 array('categories' => $categories));
2f951d86 1807
3ec163dd
EL
1808 $transaction = $DB->start_delegated_transaction();
1809
1810 $createdcategories = array();
2f951d86 1811 foreach ($params['categories'] as $category) {
3ec163dd
EL
1812 if ($category['parent']) {
1813 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1814 throw new moodle_exception('unknowcategory');
1815 }
1816 $context = context_coursecat::instance($category['parent']);
1817 } else {
1818 $context = context_system::instance();
2f951d86 1819 }
2f951d86 1820 self::validate_context($context);
3ec163dd 1821 require_capability('moodle/category:manage', $context);
2f951d86 1822
9bad61db
MG
1823 // this will validate format and throw an exception if there are errors
1824 external_validate_format($category['descriptionformat']);
3ec163dd 1825
9bad61db 1826 $newcategory = coursecat::create($category);
3ec163dd
EL
1827
1828 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
2f951d86
FS
1829 }
1830
3ec163dd
EL
1831 $transaction->allow_commit();
1832
1833 return $createdcategories;
2f951d86
FS
1834 }
1835
1836 /**
1837 * Returns description of method parameters
3ec163dd 1838 *
2f951d86
FS
1839 * @return external_function_parameters
1840 * @since Moodle 2.3
1841 */
3ec163dd
EL
1842 public static function create_categories_returns() {
1843 return new external_multiple_structure(
1844 new external_single_structure(
1845 array(
1846 'id' => new external_value(PARAM_INT, 'new category id'),
1847 'name' => new external_value(PARAM_TEXT, 'new category name'),
1848 )
1849 )
1850 );
2f951d86 1851 }
f2229c68
FS
1852
1853 /**
1854 * Returns description of method parameters
3ec163dd 1855 *
f2229c68
FS
1856 * @return external_function_parameters
1857 * @since Moodle 2.3
1858 */
1859 public static function update_categories_parameters() {
1860 return new external_function_parameters(
1861 array(
1862 'categories' => new external_multiple_structure(
1863 new external_single_structure(
1864 array(
1865 'id' => new external_value(PARAM_INT, 'course id'),
1866 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1867 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1868 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1869 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
93ce0e82 1870 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
f2229c68
FS
1871 'theme' => new external_value(PARAM_THEME,
1872 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1873 )
1874 )
1875 )
1876 )
1877 );
1878 }
1879
1880 /**
1881 * Update categories
3ec163dd 1882 *
f2229c68
FS
1883 * @param array $categories The list of categories to update
1884 * @return null
1885 * @since Moodle 2.3
1886 */
1887 public static function update_categories($categories) {
1888 global $CFG, $DB;
6e1d1ee0 1889 require_once($CFG->libdir . "/coursecatlib.php");
f2229c68
FS
1890
1891 // Validate parameters.
1892 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1893
1894 $transaction = $DB->start_delegated_transaction();
1895
1896 foreach ($params['categories'] as $cat) {
9bad61db 1897 $category = coursecat::get($cat['id']);
f2229c68
FS
1898
1899 $categorycontext = context_coursecat::instance($cat['id']);
1900 self::validate_context($categorycontext);
1901 require_capability('moodle/category:manage', $categorycontext);
1902
9bad61db
MG
1903 // this will throw an exception if descriptionformat is not valid
1904 external_validate_format($cat['descriptionformat']);
1905
1906 $category->update($cat);
f2229c68
FS
1907 }
1908
1909 $transaction->allow_commit();
1910 }
1911
1912 /**
1913 * Returns description of method result value
3ec163dd 1914 *
f2229c68 1915 * @return external_description
3ec163dd 1916 * @since Moodle 2.3
f2229c68
FS
1917 */
1918 public static function update_categories_returns() {
1919 return null;
1920 }
3ec163dd
EL
1921
1922 /**
1923 * Returns description of method parameters
1924 *
1925 * @return external_function_parameters
1926 * @since Moodle 2.3
1927 */
1928 public static function delete_categories_parameters() {
1929 return new external_function_parameters(
1930 array(
1931 'categories' => new external_multiple_structure(
1932 new external_single_structure(
1933 array(
1934 'id' => new external_value(PARAM_INT, 'category id to delete'),
1935 'newparent' => new external_value(PARAM_INT,
1936 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
1937 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
1938 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
1939 )
1940 )
1941 )
1942 )
1943 );
1944 }
1945
1946 /**
1947 * Delete categories
1948 *
1949 * @param array $categories A list of category ids
1950 * @return array
1951 * @since Moodle 2.3
1952 */
1953 public static function delete_categories($categories) {
1954 global $CFG, $DB;
1955 require_once($CFG->dirroot . "/course/lib.php");
deb65ced 1956 require_once($CFG->libdir . "/coursecatlib.php");
3ec163dd
EL
1957
1958 // Validate parameters.
1959 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1960
f823158b
EL
1961 $transaction = $DB->start_delegated_transaction();
1962
3ec163dd 1963 foreach ($params['categories'] as $category) {
deb65ced 1964 $deletecat = coursecat::get($category['id'], MUST_EXIST);
3ec163dd
EL
1965 $context = context_coursecat::instance($deletecat->id);
1966 require_capability('moodle/category:manage', $context);
1967 self::validate_context($context);
1968 self::validate_context(get_category_or_system_context($deletecat->parent));
1969
1970 if ($category['recursive']) {
1971 // If recursive was specified, then we recursively delete the category's contents.
deb65ced
MG
1972 if ($deletecat->can_delete_full()) {
1973 $deletecat->delete_full(false);
1974 } else {
1975 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1976 }
3ec163dd
EL
1977 } else {
1978 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1979 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1980 // We must move to an existing category.
1981 if (!empty($category['newparent'])) {
deb65ced 1982 $newparentcat = coursecat::get($category['newparent']);
3ec163dd 1983 } else {
deb65ced 1984 $newparentcat = coursecat::get($deletecat->parent);
3ec163dd
EL
1985 }
1986
1987 // This operation is not allowed. We must move contents to an existing category.
deb65ced 1988 if (!$newparentcat->id) {
3ec163dd
EL
1989 throw new moodle_exception('movecatcontentstoroot');
1990 }
1991
deb65ced
MG
1992 self::validate_context(context_coursecat::instance($newparentcat->id));
1993 if ($deletecat->can_move_content_to($newparentcat->id)) {
1994 $deletecat->delete_move($newparentcat->id, false);
1995 } else {
1996 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1997 }
3ec163dd
EL
1998 }
1999 }
2000
f823158b 2001 $transaction->allow_commit();
3ec163dd
EL
2002 }
2003
2004 /**
2005 * Returns description of method parameters
2006 *
2007 * @return external_function_parameters
2008 * @since Moodle 2.3
2009 */
2010 public static function delete_categories_returns() {
2011 return null;
2012 }
2013
79949c1b
MN
2014 /**
2015 * Describes the parameters for delete_modules.
2016 *
2017 * @return external_external_function_parameters
2018 * @since Moodle 2.5
2019 */
2020 public static function delete_modules_parameters() {
2021 return new external_function_parameters (
2022 array(
2023 'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID',
2024 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'),
2025 )
2026 );
2027 }
2028
2029 /**
2030 * Deletes a list of provided module instances.
2031 *
2032 * @param array $cmids the course module ids
2033 * @since Moodle 2.5
2034 */
2035 public static function delete_modules($cmids) {
2036 global $CFG, $DB;
2037
2038 // Require course file containing the course delete module function.
2039 require_once($CFG->dirroot . "/course/lib.php");
2040
2041 // Clean the parameters.
2042 $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids));
2043
2044 // Keep track of the course ids we have performed a capability check on to avoid repeating.
2045 $arrcourseschecked = array();
2046
2047 foreach ($params['cmids'] as $cmid) {
2048 // Get the course module.
2049 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST);
2050
2051 // Check if we have not yet confirmed they have permission in this course.
2052 if (!in_array($cm->course, $arrcourseschecked)) {
2053 // Ensure the current user has required permission in this course.
2054 $context = context_course::instance($cm->course);
2055 self::validate_context($context);
2056 // Add to the array.
2057 $arrcourseschecked[] = $cm->course;
2058 }
2059
2060 // Ensure they can delete this module.
2061 $modcontext = context_module::instance($cm->id);
2062 require_capability('moodle/course:manageactivities', $modcontext);
2063
2064 // Delete the module.
2065 course_delete_module($cm->id);
2066 }
2067 }
2068
2069 /**
2070 * Describes the delete_modules return value.
2071 *
2072 * @return external_single_structure
2073 * @since Moodle 2.5
2074 */
2075 public static function delete_modules_returns() {
2076 return null;
2077 }
7aed00d5
JL
2078
2079 /**
2080 * Returns description of method parameters
2081 *
2082 * @return external_function_parameters
2083 * @since Moodle 2.9
2084 */
2085 public static function view_course_parameters() {
2086 return new external_function_parameters(
2087 array(
2088 'courseid' => new external_value(PARAM_INT, 'id of the course'),
2089 'sectionnumber' => new external_value(PARAM_INT, 'section number', VALUE_DEFAULT, 0)
2090 )
2091 );
2092 }
2093
2094 /**
1c2b7882 2095 * Trigger the course viewed event.
7aed00d5
JL
2096 *
2097 * @param int $courseid id of course
2098 * @param int $sectionnumber sectionnumber (0, 1, 2...)
2099 * @return array of warnings and status result
2100 * @since Moodle 2.9
2101 * @throws moodle_exception
2102 */
2103 public static function view_course($courseid, $sectionnumber = 0) {
2104 global $CFG;
2105 require_once($CFG->dirroot . "/course/lib.php");
2106
2107 $params = self::validate_parameters(self::view_course_parameters(),
2108 array(
2109 'courseid' => $courseid,
2110 'sectionnumber' => $sectionnumber
2111 ));
2112
2113 $warnings = array();
2114
2115 $course = get_course($params['courseid']);
2116 $context = context_course::instance($course->id);
2117 self::validate_context($context);
2118
4dfef5e9
JL
2119 if (!empty($params['sectionnumber'])) {
2120
2121 // Get section details and check it exists.
2122 $modinfo = get_fast_modinfo($course);
2123 $coursesection = $modinfo->get_section_info($params['sectionnumber'], MUST_EXIST);
2124
2125 // Check user is allowed to see it.
2126 if (!$coursesection->uservisible) {
2127 require_capability('moodle/course:viewhiddensections', $context);
2128 }
2129 }
2130
7aed00d5
JL
2131 course_view($context, $params['sectionnumber']);
2132
2133 $result = array();
2134 $result['status'] = true;
2135 $result['warnings'] = $warnings;
2136 return $result;
2137 }
2138
2139 /**
2140 * Returns description of method result value
2141 *
2142 * @return external_description
2143 * @since Moodle 2.9
2144 */
2145 public static function view_course_returns() {
2146 return new external_single_structure(
2147 array(
2148 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2149 'warnings' => new external_warnings()
2150 )
2151 );
2152 }
2153
740c354f
JL
2154 /**
2155 * Returns description of method parameters
2156 *
2157 * @return external_function_parameters
2158 * @since Moodle 3.0
2159 */
2160 public static function search_courses_parameters() {
2161 return new external_function_parameters(
2162 array(
2163 'criterianame' => new external_value(PARAM_ALPHA, 'criteria name
2164 (search, modulelist (only admins), blocklist (only admins), tagid)'),
2165 'criteriavalue' => new external_value(PARAM_RAW, 'criteria value'),
2166 'page' => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0),
235ef57a
DW
2167 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
2168 'requiredcapabilities' => new external_multiple_structure(
2169 new external_value(PARAM_CAPABILITY, 'Capability string used to filter courses by permission'),
72ac2aac 2170 'Optional list of required capabilities (used to filter the list)', VALUE_DEFAULT, array()
427e3cbc
EM
2171 ),
2172 'limittoenrolled' => new external_value(PARAM_BOOL, 'limit to enrolled courses', VALUE_DEFAULT, 0),
740c354f
JL
2173 )
2174 );
2175 }
2176
80adabef
JL
2177 /**
2178 * Return the course information that is public (visible by every one)
2179 *
2180 * @param course_in_list $course course in list object
2181 * @param stdClass $coursecontext course context object
2182 * @return array the course information
2183 * @since Moodle 3.2
2184 */
2185 protected static function get_course_public_information(course_in_list $course, $coursecontext) {
2186
2187 static $categoriescache = array();
2188
2189 // Category information.
2190 if (!array_key_exists($course->category, $categoriescache)) {
2191 $categoriescache[$course->category] = coursecat::get($course->category, IGNORE_MISSING);
2192 }
2193 $category = $categoriescache[$course->category];
2194
2195 // Retrieve course overview used files.
2196 $files = array();
2197 foreach ($course->get_course_overviewfiles() as $file) {
2198 $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
2199 $file->get_filearea(), null, $file->get_filepath(),
2200 $file->get_filename())->out(false);
2201 $files[] = array(
2202 'filename' => $file->get_filename(),
2203 'fileurl' => $fileurl,
2204 'filesize' => $file->get_filesize(),
2205 'filepath' => $file->get_filepath(),
2206 'mimetype' => $file->get_mimetype(),
2207 'timemodified' => $file->get_timemodified(),
2208 );
2209 }
2210
2211 // Retrieve the course contacts,
2212 // we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services.
2213 $coursecontacts = array();
2214 foreach ($course->get_course_contacts() as $contact) {
2215 $coursecontacts[] = array(
2216 'id' => $contact['user']->id,
2217 'fullname' => $contact['username']
2218 );
2219 }
2220
2221 // Allowed enrolment methods (maybe we can self-enrol).
2222 $enroltypes = array();
2223 $instances = enrol_get_instances($course->id, true);
2224 foreach ($instances as $instance) {
2225 $enroltypes[] = $instance->enrol;
2226 }
2227
2228 // Format summary.
2229 list($summary, $summaryformat) =
2230 external_format_text($course->summary, $course->summaryformat, $coursecontext->id, 'course', 'summary', null);
2231
2232 $displayname = get_course_display_name_for_list($course);
2233 $coursereturns = array();
2234 $coursereturns['id'] = $course->id;
2235 $coursereturns['fullname'] = external_format_string($course->fullname, $coursecontext->id);
2236 $coursereturns['displayname'] = external_format_string($displayname, $coursecontext->id);
2237 $coursereturns['shortname'] = external_format_string($course->shortname, $coursecontext->id);
2238 $coursereturns['categoryid'] = $course->category;
2239 $coursereturns['categoryname'] = $category == null ? '' : $category->name;
2240 $coursereturns['summary'] = $summary;
2241 $coursereturns['summaryformat'] = $summaryformat;
2242 $coursereturns['summaryfiles'] = external_util::get_area_files($coursecontext->id, 'course', 'summary', false, false);
2243 $coursereturns['overviewfiles'] = $files;
2244 $coursereturns['contacts'] = $coursecontacts;
2245 $coursereturns['enrollmentmethods'] = $enroltypes;
2246 return $coursereturns;
2247 }
2248
740c354f
JL
2249 /**
2250 * Search courses following the specified criteria.
2251 *
2252 * @param string $criterianame Criteria name (search, modulelist (only admins), blocklist (only admins), tagid)
2253 * @param string $criteriavalue Criteria value
2254 * @param int $page Page number (for pagination)
2255 * @param int $perpage Items per page
235ef57a 2256 * @param array $requiredcapabilities Optional list of required capabilities (used to filter the list).
427e3cbc 2257 * @param int $limittoenrolled Limit to only enrolled courses
740c354f
JL
2258 * @return array of course objects and warnings
2259 * @since Moodle 3.0
2260 * @throws moodle_exception
2261 */
235ef57a
DW
2262 public static function search_courses($criterianame,
2263 $criteriavalue,
2264 $page=0,
2265 $perpage=0,
427e3cbc
EM
2266 $requiredcapabilities=array(),
2267 $limittoenrolled=0) {
740c354f
JL
2268 global $CFG;
2269 require_once($CFG->libdir . '/coursecatlib.php');
2270
2271 $warnings = array();
2272
2273 $parameters = array(
2274 'criterianame' => $criterianame,
2275 'criteriavalue' => $criteriavalue,
2276 'page' => $page,
235ef57a
DW
2277 'perpage' => $perpage,
2278 'requiredcapabilities' => $requiredcapabilities
740c354f
JL
2279 );
2280 $params = self::validate_parameters(self::search_courses_parameters(), $parameters);
1d014075 2281 self::validate_context(context_system::instance());
740c354f
JL
2282
2283 $allowedcriterianames = array('search', 'modulelist', 'blocklist', 'tagid');
2284 if (!in_array($params['criterianame'], $allowedcriterianames)) {
2285 throw new invalid_parameter_exception('Invalid value for criterianame parameter (value: '.$params['criterianame'].'),' .
2286 'allowed values are: '.implode(',', $allowedcriterianames));
2287 }
2288
2289 if ($params['criterianame'] == 'modulelist' or $params['criterianame'] == 'blocklist') {
2290 require_capability('moodle/site:config', context_system::instance());
2291 }
2292
2293 $paramtype = array(
2294 'search' => PARAM_RAW,
2295 'modulelist' => PARAM_PLUGIN,
2296 'blocklist' => PARAM_INT,
2297 'tagid' => PARAM_INT
2298 );
2299 $params['criteriavalue'] = clean_param($params['criteriavalue'], $paramtype[$params['criterianame']]);
2300
2301 // Prepare the search API options.
2302 $searchcriteria = array();
2303 $searchcriteria[$params['criterianame']] = $params['criteriavalue'];
2304
2305 $options = array();
2306 if ($params['perpage'] != 0) {
2307 $offset = $params['page'] * $params['perpage'];
2308 $options = array('offset' => $offset, 'limit' => $params['perpage']);
2309 }
2310
2311 // Search the courses.
235ef57a
DW
2312 $courses = coursecat::search_courses($searchcriteria, $options, $params['requiredcapabilities']);
2313 $totalcount = coursecat::search_courses_count($searchcriteria, $options, $params['requiredcapabilities']);
740c354f 2314
427e3cbc
EM
2315 if (!empty($limittoenrolled)) {
2316 // Get the courses where the current user has access.
2317 $enrolled = enrol_get_my_courses(array('id', 'cacherev'));
2318 }
2319
740c354f
JL
2320 $finalcourses = array();
2321 $categoriescache = array();
2322
2323 foreach ($courses as $course) {
427e3cbc
EM
2324 if (!empty($limittoenrolled)) {
2325 // Filter out not enrolled courses.
935ee1c6 2326 if (!isset($enrolled[$course->id])) {
427e3cbc
EM
2327 $totalcount--;
2328 continue;
2329 }
2330 }
740c354f
JL
2331
2332 $coursecontext = context_course::instance($course->id);
2333
80adabef 2334 $finalcourses[] = self::get_course_public_information($course, $coursecontext);
740c354f
JL
2335 }
2336
2337 return array(
2338 'total' => $totalcount,
2339 'courses' => $finalcourses,
2340 'warnings' => $warnings
2341 );
2342 }
2343
80adabef
JL
2344 /**
2345 * Returns a course structure definition
2346 *
2347 * @param boolean $onlypublicdata set to true, to retrieve only fields viewable by anyone when the course is visible
2348 * @return array the course structure
2349 * @since Moodle 3.2
2350 */
2351 protected static function get_course_structure($onlypublicdata = true) {
2352 $coursestructure = array(
2353 'id' => new external_value(PARAM_INT, 'course id'),
2354 'fullname' => new external_value(PARAM_TEXT, 'course full name'),
2355 'displayname' => new external_value(PARAM_TEXT, 'course display name'),
2356 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
2357 'categoryid' => new external_value(PARAM_INT, 'category id'),
2358 'categoryname' => new external_value(PARAM_TEXT, 'category name'),
2359 'summary' => new external_value(PARAM_RAW, 'summary'),
2360 'summaryformat' => new external_format_value('summary'),
2361 'summaryfiles' => new external_files('summary files in the summary field', VALUE_OPTIONAL),
2362 'overviewfiles' => new external_files('additional overview files attached to this course'),
2363 'contacts' => new external_multiple_structure(
2364 new external_single_structure(
2365 array(
2366 'id' => new external_value(PARAM_INT, 'contact user id'),
2367 'fullname' => new external_value(PARAM_NOTAGS, 'contact user fullname'),
2368 )
2369 ),
2370 'contact users'
2371 ),
2372 'enrollmentmethods' => new external_multiple_structure(
2373 new external_value(PARAM_PLUGIN, 'enrollment method'),
2374 'enrollment methods list'
2375 ),
2376 );
2377
2378 if (!$onlypublicdata) {
2379 $extra = array(
2380 'idnumber' => new external_value(PARAM_RAW, 'Id number', VALUE_OPTIONAL),
2381 'format' => new external_value(PARAM_PLUGIN, 'Course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
2382 'showgrades' => new external_value(PARAM_INT, '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
2383 'newsitems' => new external_value(PARAM_INT, 'Number of recent items appearing on the course page', VALUE_OPTIONAL),
2384 'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL),
2385 'maxbytes' => new external_value(PARAM_INT, 'Largest size of file that can be uploaded into', VALUE_OPTIONAL),
2386 'showreports' => new external_value(PARAM_INT, 'Are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
2387 'visible' => new external_value(PARAM_INT, '1: available to student, 0:not available', VALUE_OPTIONAL),
2388 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
2389 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
2390 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
2391 'enablecompletion' => new external_value(PARAM_INT, 'Completion enabled? 1: yes 0: no', VALUE_OPTIONAL),
2392 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
2393 'lang' => new external_value(PARAM_SAFEDIR, 'Forced course language', VALUE_OPTIONAL),
2394 'theme' => new external_value(PARAM_PLUGIN, 'Fame of the forced theme', VALUE_OPTIONAL),
2395 'sortorder' => new external_value(PARAM_INT, 'Sort order in the category', VALUE_OPTIONAL),
2396 'marker' => new external_value(PARAM_INT, 'Current course marker', VALUE_OPTIONAL),
2397 'legacyfiles' => new external_value(PARAM_INT, 'If legacy files are enabled', VALUE_OPTIONAL),
2398 'calendartype' => new external_value(PARAM_PLUGIN, 'Calendar type', VALUE_OPTIONAL),
2399 'timecreated' => new external_value(PARAM_INT, 'Time when the course was created', VALUE_OPTIONAL),
2400 'timemodified' => new external_value(PARAM_INT, 'Last time the course was updated', VALUE_OPTIONAL),
2401 'requested' => new external_value(PARAM_INT, 'If is a requested course', VALUE_OPTIONAL),
2402 'cacherev' => new external_value(PARAM_INT, 'Cache revision number', VALUE_OPTIONAL),
2403 );
2404 $coursestructure = array_merge($coursestructure, $extra);
2405 }
2406 return new external_single_structure($coursestructure);
2407 }
2408
740c354f
JL
2409 /**
2410 * Returns description of method result value
2411 *
2412 * @return external_description
2413 * @since Moodle 3.0
2414 */
2415 public static function search_courses_returns() {
740c354f
JL
2416 return new external_single_structure(
2417 array(
2418 'total' => new external_value(PARAM_INT, 'total course count'),
80adabef 2419 'courses' => new external_multiple_structure(self::get_course_structure(), 'course'),
740c354f
JL
2420 'warnings' => new external_warnings()
2421 )
2422 );
2423 }
c5158499
JL
2424
2425 /**
2426 * Returns description of method parameters
2427 *
2428 * @return external_function_parameters
2429 * @since Moodle 3.0
2430 */
2431 public static function get_course_module_parameters() {
2432 return new external_function_parameters(
2433 array(
2434 'cmid' => new external_value(PARAM_INT, 'The course module id')
2435 )
2436 );
2437 }
2438
2439 /**
2440 * Return information about a course module.
2441 *
2442 * @param int $cmid the course module id
2443 * @return array of warnings and the course module
2444 * @since Moodle 3.0
2445 * @throws moodle_exception
2446 */
2447 public static function get_course_module($cmid) {
796876b0 2448 global $CFG, $DB;
c5158499 2449
796876b0 2450 $params = self::validate_parameters(self::get_course_module_parameters(), array('cmid' => $cmid));
c5158499
JL
2451 $warnings = array();
2452
2453 $cm = get_coursemodule_from_id(null, $params['cmid'], 0, true, MUST_EXIST);
2454 $context = context_module::instance($cm->id);
2455 self::validate_context($context);
2456
2457 // If the user has permissions to manage the activity, return all the information.
2458 if (has_capability('moodle/course:manageactivities', $context)) {
796876b0
JL
2459 require_once($CFG->dirroot . '/course/modlib.php');
2460 require_once($CFG->libdir . '/gradelib.php');
2461
c5158499 2462 $info = $cm;
796876b0
JL
2463 // Get the extra information: grade, advanced grading and outcomes data.
2464 $course = get_course($cm->course);
2465 list($newcm, $newcontext, $module, $extrainfo, $cw) = get_moduleinfo_data($cm, $course);
2466 // Grades.
2467 $gradeinfo = array('grade', 'gradepass', 'gradecat');
2468 foreach ($gradeinfo as $gfield) {
2469 if (isset($extrainfo->{$gfield})) {
2470 $info->{$gfield} = $extrainfo->{$gfield};
2471 }
2472 }
2473 if (isset($extrainfo->grade) and $extrainfo->grade < 0) {
2474 $info->scale = $DB->get_field('scale', 'scale', array('id' => abs($extrainfo->grade)));
2475 }
2476 // Advanced grading.
2477 if (isset($extrainfo->_advancedgradingdata)) {
2478 $info->advancedgrading = array();
2479 foreach ($extrainfo as $key => $val) {
2480 if (strpos($key, 'advancedgradingmethod_') === 0) {
2481 $info->advancedgrading[] = array(
2482 'area' => str_replace('advancedgradingmethod_', '', $key),
2483 'method' => $val
2484 );
2485 }
2486 }
2487 }
2488 // Outcomes.
2489 foreach ($extrainfo as $key => $val) {
2490 if (strpos($key, 'outcome_') === 0) {
2491 if (!isset($info->outcomes)) {
2492 $info->outcomes = array();
2493 }
2494 $id = str_replace('outcome_', '', $key);
2495 $outcome = $outcome = grade_outcome::fetch(array('id' => $id));
2496 $info->outcomes[] = array(
2497 'id' => $id,
2498 'name' => external_format_string($outcome->get_name(), $context->id)
2499 );
2500 }
2501 }
c5158499
JL
2502 } else {
2503 // Return information is safe to show to any user.
2504 $info = new stdClass();
2505 $info->id = $cm->id;
2506 $info->course = $cm->course;
2507 $info->module = $cm->module;
2508 $info->modname = $cm->modname;
2509 $info->instance = $cm->instance;
2510 $info->section = $cm->section;
2511 $info->sectionnum = $cm->sectionnum;
2512 $info->groupmode = $cm->groupmode;
2513 $info->groupingid = $cm->groupingid;
2514 $info->completion = $cm->completion;
2515 }
2516 // Format name.
9748791b 2517 $info->name = external_format_string($cm->name, $context->id);
c5158499
JL
2518 $result = array();
2519 $result['cm'] = $info;
2520 $result['warnings'] = $warnings;
2521 return $result;
2522 }
2523
2524 /**
2525 * Returns description of method result value
2526 *
2527 * @return external_description
2528 * @since Moodle 3.0
2529 */
2530 public static function get_course_module_returns() {
2531 return new external_single_structure(
2532 array(
2533 'cm' => new external_single_structure(
2534 array(
2535 'id' => new external_value(PARAM_INT, 'The course module id'),
2536 'course' => new external_value(PARAM_INT, 'The course id'),
2537 'module' => new external_value(PARAM_INT, 'The module type id'),
9748791b 2538 'name' => new external_value(PARAM_RAW, 'The activity name'),
c5158499
JL
2539 'modname' => new external_value(PARAM_COMPONENT, 'The module component name (forum, assign, etc..)'),
2540 'instance' => new external_value(PARAM_INT, 'The activity instance id'),
2541 'section' => new external_value(PARAM_INT, 'The module section id'),
2542 'sectionnum' => new external_value(PARAM_INT, 'The module section number'),
2543 'groupmode' => new external_value(PARAM_INT, 'Group mode'),
2544 'groupingid' => new external_value(PARAM_INT, 'Grouping id'),
2545 'completion' => new external_value(PARAM_INT, 'If completion is enabled'),
2546 'idnumber' => new external_value(PARAM_RAW, 'Module id number', VALUE_OPTIONAL),
2547 'added' => new external_value(PARAM_INT, 'Time added', VALUE_OPTIONAL),
2548 'score' => new external_value(PARAM_INT, 'Score', VALUE_OPTIONAL),
2549 'indent' => new external_value(PARAM_INT, 'Indentation', VALUE_OPTIONAL),
2550 'visible' => new external_value(PARAM_INT, 'If visible', VALUE_OPTIONAL),
2551 'visibleold' => new external_value(PARAM_INT, 'Visible old', VALUE_OPTIONAL),
2552 'completiongradeitemnumber' => new external_value(PARAM_INT, 'Completion grade item', VALUE_OPTIONAL),
2553 'completionview' => new external_value(PARAM_INT, 'Completion view setting', VALUE_OPTIONAL),
2554 'completionexpected' => new external_value(PARAM_INT, 'Completion time expected', VALUE_OPTIONAL),
2555 'showdescription' => new external_value(PARAM_INT, 'If the description is showed', VALUE_OPTIONAL),
2556 'availability' => new external_value(PARAM_RAW, 'Availability settings', VALUE_OPTIONAL),
796876b0
JL
2557 'grade' => new external_value(PARAM_INT, 'Grade (max value or scale id)', VALUE_OPTIONAL),
2558 'scale' => new external_value(PARAM_TEXT, 'Scale items (if used)', VALUE_OPTIONAL),
2559 'gradepass' => new external_value(PARAM_RAW, 'Grade to pass (float)', VALUE_OPTIONAL),
2560 'gradecat' => new external_value(PARAM_INT, 'Grade category', VALUE_OPTIONAL),
2561 'advancedgrading' => new external_multiple_structure(
2562 new external_single_structure(
2563 array(
2564 'area' => new external_value(PARAM_AREA, 'Gradable area name'),
2565 'method' => new external_value(PARAM_COMPONENT, 'Grading method'),
2566 )
2567 ),
2568 'Advanced grading settings', VALUE_OPTIONAL
2569 ),
2570 'outcomes' => new external_multiple_structure(
2571 new external_single_structure(
2572 array(
2573 'id' => new external_value(PARAM_ALPHANUMEXT, 'Outcome id'),
2574 'name' => new external_value(PARAM_TEXT, 'Outcome full name'),
2575 )
2576 ),
2577 'Outcomes information', VALUE_OPTIONAL
2578 ),
c5158499
JL
2579 )
2580 ),
2581 'warnings' => new external_warnings()
2582 )
2583 );
2584 }
2585
13bb6819
JL
2586 /**
2587 * Returns description of method parameters
2588 *
2589 * @return external_function_parameters
2590 * @since Moodle 3.0
2591 */
2592 public static function get_course_module_by_instance_parameters() {
2593 return new external_function_parameters(
2594 array(
2595 'module' => new external_value(PARAM_COMPONENT, 'The module name'),
2596 'instance' => new external_value(PARAM_INT, 'The module instance id')
2597 )
2598 );
2599 }
2600
2601 /**
2602 * Return information about a course module.
2603 *
d30255a0
DM
2604 * @param string $module the module name
2605 * @param int $instance the activity instance id
13bb6819
JL
2606 * @return array of warnings and the course module
2607 * @since Moodle 3.0
2608 * @throws moodle_exception
2609 */
2610 public static function get_course_module_by_instance($module, $instance) {
2611
2612 $params = self::validate_parameters(self::get_course_module_by_instance_parameters(),
2613 array(
2614 'module' => $module,
2615 'instance' => $instance,
2616 ));
2617
2618 $warnings = array();
2619 $cm = get_coursemodule_from_instance($params['module'], $params['instance'], 0, false, MUST_EXIST);
2620
2621 return self::get_course_module($cm->id);
2622 }
2623
2624 /**
2625 * Returns description of method result value
2626 *
2627 * @return external_description
2628 * @since Moodle 3.0
2629 */
2630 public static function get_course_module_by_instance_returns() {
2631 return self::get_course_module_returns();
2632 }
2633
7c4e686f
JL
2634 /**
2635 * Returns description of method parameters
2636 *
2637 * @return external_function_parameters
2638 * @since Moodle 3.2
2639 */
2640 public static function get_activities_overview_parameters() {
2641 return new external_function_parameters(
2642 array(
2643 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')),
2644 )
2645 );
2646 }
2647
2648 /**
2649 * Return activities overview for the given courses.
2650 *
2651 * @param array $courseids a list of course ids
2652 * @return array of warnings and the activities overview
2653 * @since Moodle 3.2
2654 * @throws moodle_exception
2655 */
2656 public static function get_activities_overview($courseids) {
2657 global $USER;
2658
2659 // Parameter validation.
2660 $params = self::validate_parameters(self::get_activities_overview_parameters(), array('courseids' => $courseids));
2661 $courseoverviews = array();
2662
2663 list($courses, $warnings) = external_util::validate_courses($params['courseids']);
2664
2665 if (!empty($courses)) {
2666 // Add lastaccess to each course (required by print_overview function).
2667 // We need the complete user data, the ws server does not load a complete one.
2668 $user = get_complete_user_data('id', $USER->id);
2669 foreach ($courses as $course) {
2670 if (isset($user->lastcourseaccess[$course->id])) {
2671 $course->lastaccess = $user->lastcourseaccess[$course->id];
2672 } else {
2673 $course->lastaccess = 0;
2674 }
2675 }
2676
2677 $overviews = array();
2678 if ($modules = get_plugin_list_with_function('mod', 'print_overview')) {
2679 foreach ($modules as $fname) {
2680 $fname($courses, $overviews);
2681 }
2682 }
2683
2684 // Format output.
2685 foreach ($overviews as $courseid => $modules) {
2686 $courseoverviews[$courseid]['id'] = $courseid;
2687 $courseoverviews[$courseid]['overviews'] = array();
2688
2689 foreach ($modules as $modname => $overviewtext) {
2690 $courseoverviews[$courseid]['overviews'][] = array(
2691 'module' => $modname,
2692 'overviewtext' => $overviewtext // This text doesn't need formatting.
2693 );
2694 }
2695 }
2696 }
2697
2698 $result = array(
2699 'courses' => $courseoverviews,
2700 'warnings' => $warnings
2701 );
2702 return $result;
2703 }
2704
2705 /**
2706 * Returns description of method result value
2707 *
2708 * @return external_description
2709 * @since Moodle 3.2
2710 */
2711 public static function get_activities_overview_returns() {
2712 return new external_single_structure(
2713 array(
2714 'courses' => new external_multiple_structure(
2715 new external_single_structure(
2716 array(
2717 'id' => new external_value(PARAM_INT, 'Course id'),
2718 'overviews' => new external_multiple_structure(
2719 new external_single_structure(
2720 array(
2721 'module' => new external_value(PARAM_PLUGIN, 'Module name'),
2722 'overviewtext' => new external_value(PARAM_RAW, 'Overview text'),
2723 )
2724 )
2725 )
2726 )
2727 ), 'List of courses'
2728 ),
2729 'warnings' => new external_warnings()
2730 )
2731 );
2732 }
2733
c115ff6a
JL
2734 /**
2735 * Returns description of method parameters
2736 *
2737 * @return external_function_parameters
2738 * @since Moodle 3.2
2739 */
2740 public static function get_user_navigation_options_parameters() {
2741 return new external_function_parameters(
2742 array(
2743 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')),
2744 )
2745 );
2746 }
2747
2748 /**
2749 * Return a list of navigation options in a set of courses that are avaialable or not for the current user.
2750 *
2751 * @param array $courseids a list of course ids
2752 * @return array of warnings and the options availability
2753 * @since Moodle 3.2
2754 * @throws moodle_exception
2755 */
2756 public static function get_user_navigation_options($courseids) {
2757 global $CFG;
2758 require_once($CFG->dirroot . '/course/lib.php');
2759
2760 // Parameter validation.
2761 $params = self::validate_parameters(self::get_user_navigation_options_parameters(), array('courseids' => $courseids));
2762 $courseoptions = array();
2763
2764 list($courses, $warnings) = external_util::validate_courses($params['courseids'], array(), true);
2765
2766 if (!empty($courses)) {
2767 foreach ($courses as $course) {
2768 // Fix the context for the frontpage.
2769 if ($course->id == SITEID) {
2770 $course->context = context_system::instance();
2771 }
2772 $navoptions = course_get_user_navigation_options($course->context, $course);
2773 $options = array();
2774 foreach ($navoptions as $name => $available) {
2775 $options[] = array(
2776 'name' => $name,
2777 'available' => $available,
2778 );
2779 }
2780
2781 $courseoptions[] = array(
2782 'id' => $course->id,
2783 'options' => $options
2784 );
2785 }
2786 }
2787
2788 $result = array(
2789 'courses' => $courseoptions,
2790 'warnings' => $warnings
2791 );
2792 return $result;
2793 }
2794
2795 /**
2796 * Returns description of method result value
2797 *
2798 * @return external_description
2799 * @since Moodle 3.2
2800 */
2801 public static function get_user_navigation_options_returns() {
2802 return new external_single_structure(
2803 array(
2804 'courses' => new external_multiple_structure(
2805 new external_single_structure(
2806 array(
2807 'id' => new external_value(PARAM_INT, 'Course id'),
2808 'options' => new external_multiple_structure(
2809 new external_single_structure(
2810 array(
2811 'name' => new external_value(PARAM_ALPHANUMEXT, 'Option name'),
2812 'available' => new external_value(PARAM_BOOL, 'Whether the option is available or not'),
2813 )
2814 )
2815 )
2816 )
2817 ), 'List of courses'
2818 ),
2819 'warnings' => new external_warnings()
2820 )
2821 );
2822 }
2823
b9050b10
JL
2824 /**
2825 * Returns description of method parameters
2826 *
2827 * @return external_function_parameters
2828 * @since Moodle 3.2
2829 */
2830 public static function get_user_administration_options_parameters() {
2831 return new external_function_parameters(
2832 array(
2833 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')),
2834 )
2835 );
2836 }
2837
2838 /**
2839 * Return a list of administration options in a set of courses that are available or not for the current user.
2840 *
2841 * @param array $courseids a list of course ids
2842 * @return array of warnings and the options availability
2843 * @since Moodle 3.2
2844 * @throws moodle_exception
2845 */
2846 public static function get_user_administration_options($courseids) {
2847 global $CFG;
2848 require_once($CFG->dirroot . '/course/lib.php');
2849
2850 // Parameter validation.
2851 $params = self::validate_parameters(self::get_user_administration_options_parameters(), array('courseids' => $courseids));
2852 $courseoptions = array();
2853
2854 list($courses, $warnings) = external_util::validate_courses($params['courseids'], array(), true);
2855
2856 if (!empty($courses)) {
2857 foreach ($courses as $course) {
2858 $adminoptions = course_get_user_administration_options($course, $course->context);
2859 $options = array();
2860 foreach ($adminoptions as $name => $available) {
2861 $options[] = array(
2862 'name' => $name,
2863 'available' => $available,
2864 );
2865 }
2866
2867 $courseoptions[] = array(
2868 'id' => $course->id,
2869 'options' => $options
2870 );
2871 }
2872 }
2873
2874 $result = array(
2875 'courses' => $courseoptions,
2876 'warnings' => $warnings
2877 );
2878 return $result;
2879 }
2880
2881 /**
2882 * Returns description of method result value
2883 *
2884 * @return external_description
2885 * @since Moodle 3.2
2886 */
2887 public static function get_user_administration_options_returns() {
2888 return self::get_user_navigation_options_returns();
2889 }
80adabef
JL
2890
2891 /**
2892 * Returns description of method parameters
2893 *
2894 * @return external_function_parameters
2895 * @since Moodle 3.2
2896 */
2897 public static function get_courses_by_field_parameters() {
2898 return new external_function_parameters(
2899 array(
2900 'field' => new external_value(PARAM_ALPHA, 'The field to search can be left empty for all courses or:
2901 id: course id
2902 ids: comma separated course ids
2903 shortname: course short name
2904 idnumber: course id number
2905 category: category id the course belongs to
2906 ', VALUE_DEFAULT, ''),
2907 'value' => new external_value(PARAM_RAW, 'The value to match', VALUE_DEFAULT, '')
2908 )
2909 );
2910 }
2911
2912
2913 /**
2914 * Get courses matching a specific field (id/s, shortname, idnumber, category)
2915 *
2916 * @param string $field field name to search, or empty for all courses
2917 * @param string $value value to search
2918 * @return array list of courses and warnings
2919 * @throws invalid_parameter_exception
2920 * @since Moodle 3.2
2921 */
2922 public static function get_courses_by_field($field = '', $value = '') {
2923 global $DB, $CFG;
2924 require_once($CFG->libdir . '/coursecatlib.php');
2925
2926 $params = self::validate_parameters(self::get_courses_by_field_parameters(),
2927 array(
2928 'field' => $field,
2929 'value' => $value,
2930 )
2931 );
2932 $warnings = array();
2933
2934 if (empty($params['field'])) {
2935 $courses = $DB->get_records('course', null, 'id ASC');
2936 } else {
2937 switch ($params['field']) {
2938 case 'id':
2939 case 'category':
2940 $value = clean_param($params['value'], PARAM_INT);
2941 break;
2942 case 'ids':
2943 $value = clean_param($params['value'], PARAM_SEQUENCE);
2944 break;
2945 case 'shortname':
2946 $value = clean_param($params['value'], PARAM_TEXT);
2947 break;
2948 case 'idnumber':
2949 $value = clean_param($params['value'], PARAM_RAW);
2950 break;
2951 default:
2952 throw new invalid_parameter_exception('Invalid field name');
2953 }
2954
2955 if ($params['field'] === 'ids') {
2956 $courses = $DB->get_records_list('course', 'id', explode(',', $value), 'id ASC');
2957 } else {
2958 $courses = $DB->get_records('course', array($params['field'] => $value), 'id ASC');
2959 }
2960 }
2961
2962 $coursesdata = array();
2963 foreach ($courses as $course) {
2964 $context = context_course::instance($course->id);
2965 $canupdatecourse = has_capability('moodle/course:update', $context);
2966 $canviewhiddencourses = has_capability('moodle/course:viewhiddencourses', $context);
2967
2968 // Check if the course is visible in the site for the user.
2969 if (!$course->visible and !$canviewhiddencourses and !$canupdatecourse) {
2970 continue;
2971 }
2972 // Get the public course information, even if we are not enrolled.
2973 $courseinlist = new course_in_list($course);
2974 $coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context);
2975
2976 // Now, check if we have access to the course.
2977 try {
2978 self::validate_context($context);
2979 } catch (Exception $e) {
2980 continue;
2981 }
2982 // Return information for any user that can access the course.
2983 $coursefields = array('format', 'showgrades', 'newsitems', 'startdate', 'maxbytes', 'showreports', 'visible',
2984 'groupmode', 'groupmodeforce', 'defaultgroupingid', 'enablecompletion', 'completionnotify', 'lang', 'theme',
2985 'sortorder', 'marker');
2986
2987 // Information for managers only.
2988 if ($canupdatecourse) {
2989 $managerfields = array('idnumber', 'legacyfiles', 'calendartype', 'timecreated', 'timemodified', 'requested',
2990 'cacherev');
2991 $coursefields = array_merge($coursefields, $managerfields);
2992 }
2993
2994 // Populate fields.
2995 foreach ($coursefields as $field) {
2996 $coursesdata[$course->id][$field] = $course->{$field};
2997 }
2998 }
2999
3000 return array(
3001 'courses' => $coursesdata,
3002 'warnings' => $warnings
3003 );
3004 }
3005
3006 /**
3007 * Returns description of method result value
3008 *
3009 * @return external_description
3010 * @since Moodle 3.2
3011 */
3012 public static function get_courses_by_field_returns() {
3013 // Course structure, including not only public viewable fields.
3014 return new external_single_structure(
3015 array(
3016 'courses' => new external_multiple_structure(self::get_course_structure(false), 'Course'),
3017 'warnings' => new external_warnings()
3018 )
3019 );
3020 }
5d1017e1 3021}