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