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