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