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