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