MDL-33776 Web services: get_categories - subcategories should be checked against...
[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
88 $context = get_context_instance(CONTEXT_COURSE, $course->id);
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);
108 $sections = get_all_sections($course->id);
109
110 //for each sections (first displayed to last displayed)
111 foreach ($sections as $key => $section) {
112
113 $showsection = (has_capability('moodle/course:viewhiddensections', $context) or $section->visible or !$course->hiddensections);
114 if (!$showsection) {
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
129 foreach ($modinfo->sections[$section->section] as $cmid) { //matching /course/lib.php:print_section() logic
130 $cm = $modinfo->cms[$cmid];
131
132 // stop here if the module is not visible to the user
133 if (!$cm->uservisible) {
134 continue;
135 }
136
137 $module = array();
138
139 //common info (for people being able to see the module or availability dates)
140 $module['id'] = $cm->id;
141 $module['name'] = format_string($cm->name, true);
142 $module['modname'] = $cm->modname;
143 $module['modplural'] = $cm->modplural;
144 $module['modicon'] = $cm->get_icon_url()->out(false);
145 $module['indent'] = $cm->indent;
146
147 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
148
149 if (!empty($cm->showdescription)) {
150 $module['description'] = $cm->get_content();
151 }
152
153 //url of the module
154 $url = $cm->get_url();
155 if ($url) { //labels don't have url
156 $module['url'] = $cm->get_url()->out();
157 }
158
159 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
160 get_context_instance(CONTEXT_MODULE, $cm->id));
161 //user that can view hidden module should know about the visibility
162 $module['visible'] = $cm->visible;
163
164 //availability date (also send to user who can see hidden module when the showavailabilyt is ON)
165 if ($canupdatecourse or ($CFG->enableavailability && $canviewhidden && $cm->showavailability)) {
166 $module['availablefrom'] = $cm->availablefrom;
167 $module['availableuntil'] = $cm->availableuntil;
168 }
169
170 $baseurl = 'webservice/pluginfile.php';
171
172 //call $modulename_export_contents
173 //(each module callback take care about checking the capabilities)
174 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
175 $getcontentfunction = $cm->modname.'_export_contents';
176 if (function_exists($getcontentfunction)) {
177 if ($contents = $getcontentfunction($cm, $baseurl)) {
178 $module['contents'] = $contents;
179 }
180 }
181
182 //assign result to $sectioncontents
183 $sectioncontents[] = $module;
184
185 }
186 $sectionvalues['modules'] = $sectioncontents;
187
188 // assign result to $coursecontents
189 $coursecontents[] = $sectionvalues;
190 }
191 }
192 return $coursecontents;
193 }
194
195 /**
196 * Returns description of method result value
4615817d 197 *
ec0d6ea2 198 * @return external_description
4615817d 199 * @since Moodle 2.2
ec0d6ea2
DC
200 */
201 public static function get_course_contents_returns() {
202 return new external_multiple_structure(
203 new external_single_structure(
204 array(
205 'id' => new external_value(PARAM_INT, 'Section ID'),
206 'name' => new external_value(PARAM_TEXT, 'Section name'),
207 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
208 'summary' => new external_value(PARAM_RAW, 'Section description'),
93ce0e82 209 'summaryformat' => new external_format_value('summary'),
ec0d6ea2
DC
210 'modules' => new external_multiple_structure(
211 new external_single_structure(
212 array(
213 'id' => new external_value(PARAM_INT, 'activity id'),
214 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
215 'name' => new external_value(PARAM_TEXT, 'activity module name'),
216 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
217 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
218 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
219 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
220 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
221 'availablefrom' => new external_value(PARAM_INT, 'module availability start date', VALUE_OPTIONAL),
222 'availableuntil' => new external_value(PARAM_INT, 'module availability en date', VALUE_OPTIONAL),
223 'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
224 'contents' => new external_multiple_structure(
225 new external_single_structure(
226 array(
227 // content info
228 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
229 'filename'=> new external_value(PARAM_FILE, 'filename'),
230 'filepath'=> new external_value(PARAM_PATH, 'filepath'),
231 'filesize'=> new external_value(PARAM_INT, 'filesize'),
232 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
233 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
234 'timecreated' => new external_value(PARAM_INT, 'Time created'),
235 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
236 'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
237
238 // copyright related info
239 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
240 'author' => new external_value(PARAM_TEXT, 'Content owner'),
241 'license' => new external_value(PARAM_TEXT, 'Content license'),
242 )
243 ), VALUE_DEFAULT, array()
244 )
245 )
246 ), 'list of module'
247 )
248 )
249 )
250 );
251 }
252
6bb31e40 253 /**
254 * Returns description of method parameters
4615817d 255 *
6bb31e40 256 * @return external_function_parameters
754c2dea 257 * @since Moodle 2.3
6bb31e40 258 */
259 public static function get_courses_parameters() {
260 return new external_function_parameters(
261 array('options' => new external_single_structure(
262 array('ids' => new external_multiple_structure(
263 new external_value(PARAM_INT, 'Course id')
264 , 'List of course id. If empty return all courses
265 except front page course.',
266 VALUE_OPTIONAL)
267 ), 'options - operator OR is used', VALUE_DEFAULT, array())
268 )
269 );
270 }
271
272 /**
273 * Get courses
4615817d
JM
274 *
275 * @param array $options It contains an array (list of ids)
6bb31e40 276 * @return array
4615817d 277 * @since Moodle 2.2
6bb31e40 278 */
3297d575 279 public static function get_courses($options = array()) {
6bb31e40 280 global $CFG, $DB;
281 require_once($CFG->dirroot . "/course/lib.php");
282
283 //validate parameter
284 $params = self::validate_parameters(self::get_courses_parameters(),
285 array('options' => $options));
286
287 //retrieve courses
288 if (!key_exists('ids', $params['options'])
289 or empty($params['options']['ids'])) {
290 $courses = $DB->get_records('course');
291 } else {
292 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
293 }
294
295 //create return value
296 $coursesinfo = array();
297 foreach ($courses as $course) {
298
299 // now security checks
300 $context = get_context_instance(CONTEXT_COURSE, $course->id);
301 try {
302 self::validate_context($context);
303 } catch (Exception $e) {
304 $exceptionparam = new stdClass();
305 $exceptionparam->message = $e->getMessage();
306 $exceptionparam->courseid = $course->id;
96d3b93b 307 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
6bb31e40 308 }
309 require_capability('moodle/course:view', $context);
310
311 $courseinfo = array();
312 $courseinfo['id'] = $course->id;
313 $courseinfo['fullname'] = $course->fullname;
314 $courseinfo['shortname'] = $course->shortname;
315 $courseinfo['categoryid'] = $course->category;
93ce0e82
JM
316 list($courseinfo['summary'], $courseinfo['summaryformat']) =
317 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
6bb31e40 318 $courseinfo['format'] = $course->format;
319 $courseinfo['startdate'] = $course->startdate;
320 $courseinfo['numsections'] = $course->numsections;
321
322 //some field should be returned only if the user has update permission
323 $courseadmin = has_capability('moodle/course:update', $context);
324 if ($courseadmin) {
325 $courseinfo['categorysortorder'] = $course->sortorder;
326 $courseinfo['idnumber'] = $course->idnumber;
327 $courseinfo['showgrades'] = $course->showgrades;
328 $courseinfo['showreports'] = $course->showreports;
329 $courseinfo['newsitems'] = $course->newsitems;
330 $courseinfo['visible'] = $course->visible;
331 $courseinfo['maxbytes'] = $course->maxbytes;
332 $courseinfo['hiddensections'] = $course->hiddensections;
333 $courseinfo['groupmode'] = $course->groupmode;
334 $courseinfo['groupmodeforce'] = $course->groupmodeforce;
335 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
336 $courseinfo['lang'] = $course->lang;
337 $courseinfo['timecreated'] = $course->timecreated;
338 $courseinfo['timemodified'] = $course->timemodified;
339 $courseinfo['forcetheme'] = $course->theme;
340 $courseinfo['enablecompletion'] = $course->enablecompletion;
341 $courseinfo['completionstartonenrol'] = $course->completionstartonenrol;
342 $courseinfo['completionnotify'] = $course->completionnotify;
343 }
344
345 if ($courseadmin or $course->visible
346 or has_capability('moodle/course:viewhiddencourses', $context)) {
347 $coursesinfo[] = $courseinfo;
348 }
349 }
350
351 return $coursesinfo;
352 }
353
354 /**
355 * Returns description of method result value
4615817d 356 *
6bb31e40 357 * @return external_description
4615817d 358 * @since Moodle 2.2
6bb31e40 359 */
360 public static function get_courses_returns() {
361 return new external_multiple_structure(
362 new external_single_structure(
363 array(
364 'id' => new external_value(PARAM_INT, 'course id'),
365 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
366 'categoryid' => new external_value(PARAM_INT, 'category id'),
367 'categorysortorder' => new external_value(PARAM_INT,
368 'sort order into the category', VALUE_OPTIONAL),
369 'fullname' => new external_value(PARAM_TEXT, 'full name'),
370 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
371 'summary' => new external_value(PARAM_RAW, 'summary'),
93ce0e82 372 'summaryformat' => new external_format_value('summary'),
aff24313 373 'format' => new external_value(PARAM_PLUGIN,
6bb31e40 374 'course format: weeks, topics, social, site,..'),
375 'showgrades' => new external_value(PARAM_INT,
376 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
377 'newsitems' => new external_value(PARAM_INT,
378 'number of recent items appearing on the course page', VALUE_OPTIONAL),
379 'startdate' => new external_value(PARAM_INT,
380 'timestamp when the course start'),
381 'numsections' => new external_value(PARAM_INT, 'number of weeks/topics'),
382 'maxbytes' => new external_value(PARAM_INT,
383 'largest size of file that can be uploaded into the course',
384 VALUE_OPTIONAL),
385 'showreports' => new external_value(PARAM_INT,
386 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
387 'visible' => new external_value(PARAM_INT,
388 '1: available to student, 0:not available', VALUE_OPTIONAL),
389 'hiddensections' => new external_value(PARAM_INT,
390 'How the hidden sections in the course are displayed to students',
3ec163dd
EL
391 VALUE_OPTIONAL),
392 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
393 VALUE_OPTIONAL),
394 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
395 VALUE_OPTIONAL),
396 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
397 VALUE_OPTIONAL),
398 'timecreated' => new external_value(PARAM_INT,
399 'timestamp when the course have been created', VALUE_OPTIONAL),
400 'timemodified' => new external_value(PARAM_INT,
401 'timestamp when the course have been modified', VALUE_OPTIONAL),
402 'enablecompletion' => new external_value(PARAM_INT,
403 'Enabled, control via completion and activity settings. Disbaled,
404 not shown in activity settings.',
405 VALUE_OPTIONAL),
406 'completionstartonenrol' => new external_value(PARAM_INT,
407 '1: begin tracking a student\'s progress in course completion
408 after course enrolment. 0: does not',
409 VALUE_OPTIONAL),
410 'completionnotify' => new external_value(PARAM_INT,
411 '1: yes 0: no', VALUE_OPTIONAL),
412 'lang' => new external_value(PARAM_SAFEDIR,
413 'forced course language', VALUE_OPTIONAL),
414 'forcetheme' => new external_value(PARAM_PLUGIN,
415 'name of the force theme', VALUE_OPTIONAL),
416 ), 'course'
479a5db1 417 )
479a5db1
FS
418 );
419 }
420
6bb31e40 421 /**
422 * Returns description of method parameters
4615817d 423 *
6bb31e40 424 * @return external_function_parameters
4615817d 425 * @since Moodle 2.2
6bb31e40 426 */
427 public static function create_courses_parameters() {
428 $courseconfig = get_config('moodlecourse'); //needed for many default values
429 return new external_function_parameters(
430 array(
431 'courses' => new external_multiple_structure(
432 new external_single_structure(
433 array(
434 'fullname' => new external_value(PARAM_TEXT, 'full name'),
435 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
436 'categoryid' => new external_value(PARAM_INT, 'category id'),
437 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
438 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
93ce0e82 439 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
aff24313 440 'format' => new external_value(PARAM_PLUGIN,
6bb31e40 441 'course format: weeks, topics, social, site,..',
442 VALUE_DEFAULT, $courseconfig->format),
443 'showgrades' => new external_value(PARAM_INT,
444 '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
445 $courseconfig->showgrades),
446 'newsitems' => new external_value(PARAM_INT,
447 'number of recent items appearing on the course page',
448 VALUE_DEFAULT, $courseconfig->newsitems),
449 'startdate' => new external_value(PARAM_INT,
450 'timestamp when the course start', VALUE_OPTIONAL),
451 'numsections' => new external_value(PARAM_INT, 'number of weeks/topics',
452 VALUE_DEFAULT, $courseconfig->numsections),
453 'maxbytes' => new external_value(PARAM_INT,
454 'largest size of file that can be uploaded into the course',
455 VALUE_DEFAULT, $courseconfig->maxbytes),
456 'showreports' => new external_value(PARAM_INT,
457 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
458 $courseconfig->showreports),
459 'visible' => new external_value(PARAM_INT,
460 '1: available to student, 0:not available', VALUE_OPTIONAL),
461 'hiddensections' => new external_value(PARAM_INT,
462 'How the hidden sections in the course are displayed to students',
463 VALUE_DEFAULT, $courseconfig->hiddensections),
464 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
465 VALUE_DEFAULT, $courseconfig->groupmode),
466 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
467 VALUE_DEFAULT, $courseconfig->groupmodeforce),
468 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
469 VALUE_DEFAULT, 0),
470 'enablecompletion' => new external_value(PARAM_INT,
8a6b1193 471 'Enabled, control via completion and activity settings. Disabled,
6bb31e40 472 not shown in activity settings.',
473 VALUE_OPTIONAL),
474 'completionstartonenrol' => new external_value(PARAM_INT,
475 '1: begin tracking a student\'s progress in course completion after
476 course enrolment. 0: does not',
477 VALUE_OPTIONAL),
478 'completionnotify' => new external_value(PARAM_INT,
479 '1: yes 0: no', VALUE_OPTIONAL),
aff24313 480 'lang' => new external_value(PARAM_SAFEDIR,
6bb31e40 481 'forced course language', VALUE_OPTIONAL),
aff24313 482 'forcetheme' => new external_value(PARAM_PLUGIN,
6bb31e40 483 'name of the force theme', VALUE_OPTIONAL),
484 )
485 ), 'courses to create'
486 )
487 )
488 );
489 }
490
491 /**
492 * Create courses
4615817d 493 *
6bb31e40 494 * @param array $courses
495 * @return array courses (id and shortname only)
4615817d 496 * @since Moodle 2.2
6bb31e40 497 */
498 public static function create_courses($courses) {
499 global $CFG, $DB;
500 require_once($CFG->dirroot . "/course/lib.php");
501 require_once($CFG->libdir . '/completionlib.php');
502
6bb31e40 503 $params = self::validate_parameters(self::create_courses_parameters(),
504 array('courses' => $courses));
505
506 $availablethemes = get_plugin_list('theme');
507 $availablelangs = get_string_manager()->get_list_of_translations();
508
509 $transaction = $DB->start_delegated_transaction();
510
511 foreach ($params['courses'] as $course) {
512
513 // Ensure the current user is allowed to run this function
514 $context = get_context_instance(CONTEXT_COURSECAT, $course['categoryid']);
515 try {
516 self::validate_context($context);
517 } catch (Exception $e) {
518 $exceptionparam = new stdClass();
519 $exceptionparam->message = $e->getMessage();
520 $exceptionparam->catid = $course['categoryid'];
96d3b93b 521 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
6bb31e40 522 }
523 require_capability('moodle/course:create', $context);
524
525 // Make sure lang is valid
526 if (key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
96d3b93b 527 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
6bb31e40 528 }
529
530 // Make sure theme is valid
531 if (key_exists('forcetheme', $course)) {
532 if (!empty($CFG->allowcoursethemes)) {
533 if (empty($availablethemes[$course['forcetheme']])) {
96d3b93b 534 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
6bb31e40 535 } else {
536 $course['theme'] = $course['forcetheme'];
537 }
538 }
539 }
540
541 //force visibility if ws user doesn't have the permission to set it
542 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
543 if (!has_capability('moodle/course:visibility', $context)) {
544 $course['visible'] = $category->visible;
545 }
546
547 //set default value for completion
8a6b1193 548 $courseconfig = get_config('moodlecourse');
6bb31e40 549 if (completion_info::is_enabled_for_site()) {
550 if (!key_exists('enablecompletion', $course)) {
8a6b1193 551 $course['enablecompletion'] = $courseconfig->enablecompletion;
6bb31e40 552 }
553 if (!key_exists('completionstartonenrol', $course)) {
8a6b1193 554 $course['completionstartonenrol'] = $courseconfig->completionstartonenrol;
6bb31e40 555 }
556 } else {
557 $course['enablecompletion'] = 0;
558 $course['completionstartonenrol'] = 0;
559 }
560
561 $course['category'] = $course['categoryid'];
562
93ce0e82
JM
563 // Summary format.
564 $course['summaryformat'] = external_validate_format($course['summaryformat']);
565
6bb31e40 566 //Note: create_course() core function check shortname, idnumber, category
567 $course['id'] = create_course((object) $course)->id;
568
569 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
570 }
571
572 $transaction->allow_commit();
573
574 return $resultcourses;
575 }
576
577 /**
578 * Returns description of method result value
4615817d 579 *
6bb31e40 580 * @return external_description
4615817d 581 * @since Moodle 2.2
6bb31e40 582 */
583 public static function create_courses_returns() {
584 return new external_multiple_structure(
585 new external_single_structure(
586 array(
587 'id' => new external_value(PARAM_INT, 'course id'),
588 'shortname' => new external_value(PARAM_TEXT, 'short name'),
589 )
590 )
591 );
592 }
593
63a85dc7
JL
594 /**
595 * Returns description of method parameters
3ec163dd 596 *
63a85dc7 597 * @return external_function_parameters
3ec163dd 598 * @since Moodle 2.2
63a85dc7
JL
599 */
600 public static function delete_courses_parameters() {
601 return new external_function_parameters(
602 array(
603 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
604 )
605 );
606 }
607
608 /**
609 * Delete courses
3ec163dd 610 *
63a85dc7 611 * @param array $courseids A list of course ids
3ec163dd 612 * @since Moodle 2.2
63a85dc7
JL
613 */
614 public static function delete_courses($courseids) {
615 global $CFG, $DB;
616 require_once($CFG->dirroot."/course/lib.php");
617
618 // Parameter validation.
619 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
620
621 $transaction = $DB->start_delegated_transaction();
622
623 foreach ($params['courseids'] as $courseid) {
624 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
625
626 // Check if the context is valid.
627 $coursecontext = context_course::instance($course->id);
628 self::validate_context($coursecontext);
629
630 // Check if the current user has enought permissions.
631 if (!can_delete_course($courseid)) {
d6ebe011
JM
632 throw new moodle_exception('cannotdeletecategorycourse', 'error',
633 '', format_string($course->fullname)." (id: $courseid)");
63a85dc7
JL
634 }
635
636 delete_course($course, false);
637 }
638
639 $transaction->allow_commit();
640
641 return null;
642 }
643
644 /**
645 * Returns description of method result value
3ec163dd 646 *
63a85dc7 647 * @return external_description
3ec163dd 648 * @since Moodle 2.2
63a85dc7
JL
649 */
650 public static function delete_courses_returns() {
651 return null;
652 }
653
3dc1d76e
JL
654 /**
655 * Returns description of method parameters
656 *
657 * @return external_function_parameters
658 * @since Moodle 2.3
659 */
660 public static function duplicate_course_parameters() {
661 return new external_function_parameters(
662 array(
663 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
664 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
665 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
666 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
667 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
668 'options' => new external_multiple_structure(
669 new external_single_structure(
670 array(
9aa84e91
JL
671 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
672 "activities" (int) Include course activites (default to 1 that is equal to yes),
673 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
674 "filters" (int) Include course filters (default to 1 that is equal to yes),
675 "users" (int) Include users (default to 0 that is equal to no),
676 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
677 "user_files" (int) Include user files (default to 0 that is equal to no),
678 "comments" (int) Include user comments (default to 0 that is equal to no),
679 "completion_information" (int) Include user course completion information (default to 0 that is equal to no),
680 "logs" (int) Include course logs (default to 0 that is equal to no),
681 "histories" (int) Include histories (default to 0 that is equal to no)'
682 ),
683 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
684 )
3dc1d76e
JL
685 )
686 ), VALUE_DEFAULT, array()
687 ),
688 )
689 );
690 }
691
692 /**
693 * Duplicate a course
694 *
695 * @param int $courseid
696 * @param string $fullname Duplicated course fullname
697 * @param string $shortname Duplicated course shortname
698 * @param int $categoryid Duplicated course parent category id
699 * @param int $visible Duplicated course availability
700 * @param array $options List of backup options
701 * @return array New course info
702 * @since Moodle 2.3
703 */
3297d575 704 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
3dc1d76e
JL
705 global $CFG, $USER, $DB;
706 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
707 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
708
709 // Parameter validation.
710 $params = self::validate_parameters(
711 self::duplicate_course_parameters(),
712 array(
713 'courseid' => $courseid,
714 'fullname' => $fullname,
715 'shortname' => $shortname,
716 'categoryid' => $categoryid,
717 'visible' => $visible,
718 'options' => $options
719 )
720 );
721
3ec163dd
EL
722 // Context validation.
723
724 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
725 throw new moodle_exception('invalidcourseid', 'error', '', $params['courseid']);
726 }
727
728 // Category where duplicated course is going to be created.
729 $categorycontext = context_coursecat::instance($params['categoryid']);
730 self::validate_context($categorycontext);
731
732 // Course to be duplicated.
733 $coursecontext = context_course::instance($course->id);
734 self::validate_context($coursecontext);
735
736 $backupdefaults = array(
737 'activities' => 1,
738 'blocks' => 1,
739 'filters' => 1,
740 'users' => 0,
741 'role_assignments' => 0,
742 'user_files' => 0,
743 'comments' => 0,
744 'completion_information' => 0,
745 'logs' => 0,
746 'histories' => 0
747 );
748
749 $backupsettings = array();
750 // Check for backup and restore options.
751 if (!empty($params['options'])) {
752 foreach ($params['options'] as $option) {
753
754 // Strict check for a correct value (allways 1 or 0, true or false).
755 $value = clean_param($option['value'], PARAM_INT);
756
757 if ($value !== 0 and $value !== 1) {
758 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
759 }
760
761 if (!isset($backupdefaults[$option['name']])) {
762 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
763 }
764
765 $backupsettings[$option['name']] = $value;
766 }
767 }
768
769 // Capability checking.
770
771 // The backup controller check for this currently, this may be redundant.
772 require_capability('moodle/course:create', $categorycontext);
773 require_capability('moodle/restore:restorecourse', $categorycontext);
774 require_capability('moodle/backup:backupcourse', $coursecontext);
775
776 if (!empty($backupsettings['users'])) {
777 require_capability('moodle/backup:userinfo', $coursecontext);
778 require_capability('moodle/restore:userinfo', $categorycontext);
779 }
780
781 // Check if the shortname is used.
782 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
783 foreach ($foundcourses as $foundcourse) {
784 $foundcoursenames[] = $foundcourse->fullname;
785 }
786
787 $foundcoursenamestring = implode(',', $foundcoursenames);
788 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
789 }
790
791 // Backup the course.
792
793 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
794 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
795
796 foreach ($backupsettings as $name => $value) {
797 $bc->get_plan()->get_setting($name)->set_value($value);
798 }
799
800 $backupid = $bc->get_backupid();
801 $backupbasepath = $bc->get_plan()->get_basepath();
802
803 $bc->execute_plan();
804 $results = $bc->get_results();
805 $file = $results['backup_destination'];
806
807 $bc->destroy();
808
809 // Restore the backup immediately.
810
811 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
812 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
813 $file->extract_to_pathname(get_file_packer(), $backupbasepath);
814 }
815
816 // Create new course.
817 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
818
819 $rc = new restore_controller($backupid, $newcourseid,
820 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
821
822 foreach ($backupsettings as $name => $value) {
823 $setting = $rc->get_plan()->get_setting($name);
824 if ($setting->get_status() == backup_setting::NOT_LOCKED) {
825 $setting->set_value($value);
826 }
827 }
828
829 if (!$rc->execute_precheck()) {
830 $precheckresults = $rc->get_precheck_results();
831 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
832 if (empty($CFG->keeptempdirectoriesonbackup)) {
833 fulldelete($backupbasepath);
834 }
835
836 $errorinfo = '';
837
838 foreach ($precheckresults['errors'] as $error) {
839 $errorinfo .= $error;
840 }
841
842 if (array_key_exists('warnings', $precheckresults)) {
843 foreach ($precheckresults['warnings'] as $warning) {
844 $errorinfo .= $warning;
845 }
846 }
847
848 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
849 }
850 }
851
852 $rc->execute_plan();
853 $rc->destroy();
854
855 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
856 $course->fullname = $params['fullname'];
857 $course->shortname = $params['shortname'];
858 $course->visible = $params['visible'];
859
860 // Set shortname and fullname back.
861 $DB->update_record('course', $course);
862
863 if (empty($CFG->keeptempdirectoriesonbackup)) {
864 fulldelete($backupbasepath);
865 }
866
867 // Delete the course backup file created by this WebService. Originally located in the course backups area.
868 $file->delete();
869
870 return array('id' => $course->id, 'shortname' => $course->shortname);
871 }
872
873 /**
874 * Returns description of method result value
875 *
876 * @return external_description
877 * @since Moodle 2.3
878 */
879 public static function duplicate_course_returns() {
880 return new external_single_structure(
881 array(
882 'id' => new external_value(PARAM_INT, 'course id'),
883 'shortname' => new external_value(PARAM_TEXT, 'short name'),
884 )
885 );
886 }
887
888 /**
889 * Returns description of method parameters
890 *
891 * @return external_function_parameters
892 * @since Moodle 2.3
893 */
894 public static function get_categories_parameters() {
895 return new external_function_parameters(
896 array(
897 'criteria' => new external_multiple_structure(
898 new external_single_structure(
899 array(
900 'key' => new external_value(PARAM_ALPHA,
901 'The category column to search, expected keys (value format) are:'.
902 '"id" (int) the category id,'.
903 '"name" (string) the category name,'.
904 '"parent" (int) the parent category id,'.
905 '"idnumber" (string) category idnumber'.
906 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
6b5ce20c
JM
907 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
908 then the function return all categories that the user can see.'.
3ec163dd 909 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
6b5ce20c 910 '"theme" (string) only return the categories having this theme'.
3ec163dd
EL
911 ' - user must have \'moodle/category:manage\' to search on theme'),
912 'value' => new external_value(PARAM_RAW, 'the value to match')
913 )
7a384506 914 ), 'criteria', VALUE_DEFAULT, array()
3ec163dd
EL
915 ),
916 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
917 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
918 )
919 );
920 }
921
922 /**
923 * Get categories
924 *
925 * @param array $criteria Criteria to match the results
926 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
927 * @return array list of categories
928 * @since Moodle 2.3
929 */
930 public static function get_categories($criteria = array(), $addsubcategories = true) {
931 global $CFG, $DB;
932 require_once($CFG->dirroot . "/course/lib.php");
933
934 // Validate parameters.
935 $params = self::validate_parameters(self::get_categories_parameters(),
936 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
937
938 // Retrieve the categories.
939 $categories = array();
940 if (!empty($params['criteria'])) {
941
942 $conditions = array();
943 $wheres = array();
944 foreach ($params['criteria'] as $crit) {
945 $key = trim($crit['key']);
946
947 // Trying to avoid duplicate keys.
948 if (!isset($conditions[$key])) {
3dc1d76e 949
3ec163dd
EL
950 $context = context_system::instance();
951 $value = null;
952 switch ($key) {
953 case 'id':
954 $value = clean_param($crit['value'], PARAM_INT);
955 break;
3dc1d76e 956
3ec163dd
EL
957 case 'idnumber':
958 if (has_capability('moodle/category:manage', $context)) {
959 $value = clean_param($crit['value'], PARAM_RAW);
960 } else {
961 // We must throw an exception.
962 // Otherwise the dev client would think no idnumber exists.
963 throw new moodle_exception('criteriaerror',
964 'webservice', '', null,
965 'You don\'t have the permissions to search on the "idnumber" field.');
966 }
967 break;
3dc1d76e 968
3ec163dd
EL
969 case 'name':
970 $value = clean_param($crit['value'], PARAM_TEXT);
971 break;
3dc1d76e 972
3ec163dd
EL
973 case 'parent':
974 $value = clean_param($crit['value'], PARAM_INT);
975 break;
9aa84e91 976
3ec163dd
EL
977 case 'visible':
978 if (has_capability('moodle/category:manage', $context)
979 or has_capability('moodle/category:viewhiddencategories',
980 context_system::instance())) {
981 $value = clean_param($crit['value'], PARAM_INT);
982 } else {
983 throw new moodle_exception('criteriaerror',
984 'webservice', '', null,
985 'You don\'t have the permissions to search on the "visible" field.');
986 }
987 break;
9aa84e91 988
3ec163dd
EL
989 case 'theme':
990 if (has_capability('moodle/category:manage', $context)) {
991 $value = clean_param($crit['value'], PARAM_THEME);
992 } else {
993 throw new moodle_exception('criteriaerror',
994 'webservice', '', null,
995 'You don\'t have the permissions to search on the "theme" field.');
996 }
997 break;
9aa84e91 998
3ec163dd
EL
999 default:
1000 throw new moodle_exception('criteriaerror',
1001 'webservice', '', null,
1002 'You can not search on this criteria: ' . $key);
1003 }
9aa84e91 1004
3ec163dd
EL
1005 if (isset($value)) {
1006 $conditions[$key] = $crit['value'];
1007 $wheres[] = $key . " = :" . $key;
1008 }
9aa84e91 1009 }
9aa84e91 1010 }
9aa84e91 1011
3ec163dd
EL
1012 if (!empty($wheres)) {
1013 $wheres = implode(" AND ", $wheres);
3dc1d76e 1014
3ec163dd 1015 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
3dc1d76e 1016
3ec163dd
EL
1017 // Retrieve its sub subcategories (all levels).
1018 if ($categories and !empty($params['addsubcategories'])) {
1019 $newcategories = array();
9aa84e91 1020
6b5ce20c
JM
1021 // Check if we required visible/theme checks.
1022 $additionalselect = '';
1023 $additionalparams = array();
1024 if (isset($conditions['visible'])) {
1025 $additionalselect .= ' AND visible = :visible';
1026 $additionalparams['visible'] = $conditions['visible'];
1027 }
1028 if (isset($conditions['theme'])) {
1029 $additionalselect .= ' AND theme= :theme';
1030 $additionalparams['theme'] = $conditions['theme'];
1031 }
1032
3ec163dd 1033 foreach ($categories as $category) {
6b5ce20c
JM
1034 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1035 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1036 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
3ec163dd
EL
1037 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1038 }
1039 $categories = $categories + $newcategories;
1040 }
3dc1d76e
JL
1041 }
1042
3ec163dd
EL
1043 } else {
1044 // Retrieve all categories in the database.
1045 $categories = $DB->get_records('course_categories');
3dc1d76e
JL
1046 }
1047
3ec163dd
EL
1048 // The not returned categories. key => category id, value => reason of exclusion.
1049 $excludedcats = array();
3dc1d76e 1050
3ec163dd
EL
1051 // The returned categories.
1052 $categoriesinfo = array();
6c7d3e31 1053
3ec163dd
EL
1054 // We need to sort the categories by path.
1055 // The parent cats need to be checked by the algo first.
1056 usort($categories, "core_course_external::compare_categories_by_path");
3dc1d76e 1057
3ec163dd 1058 foreach ($categories as $category) {
3dc1d76e 1059
3ec163dd
EL
1060 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1061 $parents = explode('/', $category->path);
1062 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1063 foreach ($parents as $parentid) {
1064 // Note: when the parent exclusion was due to the context,
1065 // the sub category could still be returned.
1066 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1067 $excludedcats[$category->id] = 'parent';
1068 }
1069 }
9aa84e91 1070
3ec163dd
EL
1071 // Check category depth is <= maxdepth (do not check for user who can manage categories).
1072 if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
1073 and !has_capability('moodle/category:manage', $context)) {
1074 $excludedcats[$category->id] = 'depth';
1075 }
3dc1d76e 1076
3ec163dd
EL
1077 // Check the user can use the category context.
1078 $context = context_coursecat::instance($category->id);
1079 try {
1080 self::validate_context($context);
1081 } catch (Exception $e) {
1082 $excludedcats[$category->id] = 'context';
3dc1d76e 1083
3ec163dd
EL
1084 // If it was the requested category then throw an exception.
1085 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1086 $exceptionparam = new stdClass();
1087 $exceptionparam->message = $e->getMessage();
1088 $exceptionparam->catid = $category->id;
1089 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1090 }
9aa84e91 1091 }
3dc1d76e 1092
3ec163dd
EL
1093 // Return the category information.
1094 if (!isset($excludedcats[$category->id])) {
3dc1d76e 1095
3ec163dd
EL
1096 // Final check to see if the category is visible to the user.
1097 if ($category->visible
1098 or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1099 or has_capability('moodle/category:manage', $context)) {
3dc1d76e 1100
3ec163dd
EL
1101 $categoryinfo = array();
1102 $categoryinfo['id'] = $category->id;
1103 $categoryinfo['name'] = $category->name;
93ce0e82
JM
1104 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1105 external_format_text($category->description, $category->descriptionformat,
1106 $context->id, 'coursecat', 'description', null);
3ec163dd
EL
1107 $categoryinfo['parent'] = $category->parent;
1108 $categoryinfo['sortorder'] = $category->sortorder;
1109 $categoryinfo['coursecount'] = $category->coursecount;
1110 $categoryinfo['depth'] = $category->depth;
1111 $categoryinfo['path'] = $category->path;
3dc1d76e 1112
3ec163dd
EL
1113 // Some fields only returned for admin.
1114 if (has_capability('moodle/category:manage', $context)) {
1115 $categoryinfo['idnumber'] = $category->idnumber;
1116 $categoryinfo['visible'] = $category->visible;
1117 $categoryinfo['visibleold'] = $category->visibleold;
1118 $categoryinfo['timemodified'] = $category->timemodified;
1119 $categoryinfo['theme'] = $category->theme;
3dc1d76e 1120 }
3dc1d76e 1121
3ec163dd
EL
1122 $categoriesinfo[] = $categoryinfo;
1123 } else {
1124 $excludedcats[$category->id] = 'visibility';
1125 }
3dc1d76e
JL
1126 }
1127 }
1128
3ec163dd
EL
1129 // Sorting the resulting array so it looks a bit better for the client developer.
1130 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
3dc1d76e 1131
3ec163dd
EL
1132 return $categoriesinfo;
1133 }
3dc1d76e 1134
3ec163dd
EL
1135 /**
1136 * Sort categories array by path
1137 * private function: only used by get_categories
1138 *
1139 * @param array $category1
1140 * @param array $category2
1141 * @return int result of strcmp
1142 * @since Moodle 2.3
1143 */
1144 private static function compare_categories_by_path($category1, $category2) {
1145 return strcmp($category1->path, $category2->path);
1146 }
6c7d3e31 1147
3ec163dd
EL
1148 /**
1149 * Sort categories array by sortorder
1150 * private function: only used by get_categories
1151 *
1152 * @param array $category1
1153 * @param array $category2
1154 * @return int result of strcmp
1155 * @since Moodle 2.3
1156 */
1157 private static function compare_categories_by_sortorder($category1, $category2) {
1158 return strcmp($category1['sortorder'], $category2['sortorder']);
3dc1d76e
JL
1159 }
1160
1161 /**
1162 * Returns description of method result value
1163 *
1164 * @return external_description
1165 * @since Moodle 2.3
1166 */
3ec163dd
EL
1167 public static function get_categories_returns() {
1168 return new external_multiple_structure(
1169 new external_single_structure(
1170 array(
1171 'id' => new external_value(PARAM_INT, 'category id'),
1172 'name' => new external_value(PARAM_TEXT, 'category name'),
1173 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1174 'description' => new external_value(PARAM_RAW, 'category description'),
93ce0e82 1175 'descriptionformat' => new external_format_value('description'),
3ec163dd
EL
1176 'parent' => new external_value(PARAM_INT, 'parent category id'),
1177 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1178 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1179 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1180 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1181 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1182 'depth' => new external_value(PARAM_INT, 'category depth'),
1183 'path' => new external_value(PARAM_TEXT, 'category path'),
1184 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1185 ), 'List of categories'
3dc1d76e
JL
1186 )
1187 );
1188 }
1189
2f951d86
FS
1190 /**
1191 * Returns description of method parameters
3ec163dd 1192 *
2f951d86
FS
1193 * @return external_function_parameters
1194 * @since Moodle 2.3
1195 */
3ec163dd 1196 public static function create_categories_parameters() {
2f951d86
FS
1197 return new external_function_parameters(
1198 array(
1199 'categories' => new external_multiple_structure(
3ec163dd
EL
1200 new external_single_structure(
1201 array(
1202 'name' => new external_value(PARAM_TEXT, 'new category name'),
1203 'parent' => new external_value(PARAM_INT,
9615b623
JM
1204 'the parent category id inside which the new category will be created
1205 - set to 0 for a root category',
1206 VALUE_DEFAULT, 0),
3ec163dd
EL
1207 'idnumber' => new external_value(PARAM_RAW,
1208 'the new category idnumber', VALUE_OPTIONAL),
1209 'description' => new external_value(PARAM_RAW,
1210 'the new category description', VALUE_OPTIONAL),
93ce0e82 1211 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
3ec163dd
EL
1212 'theme' => new external_value(PARAM_THEME,
1213 'the new category theme. This option must be enabled on moodle',
1214 VALUE_OPTIONAL),
2f951d86
FS
1215 )
1216 )
1217 )
1218 )
1219 );
1220 }
1221
1222 /**
3ec163dd
EL
1223 * Create categories
1224 *
1225 * @param array $categories - see create_categories_parameters() for the array structure
1226 * @return array - see create_categories_returns() for the array structure
2f951d86
FS
1227 * @since Moodle 2.3
1228 */
3ec163dd 1229 public static function create_categories($categories) {
2f951d86
FS
1230 global $CFG, $DB;
1231 require_once($CFG->dirroot . "/course/lib.php");
1232
3ec163dd
EL
1233 $params = self::validate_parameters(self::create_categories_parameters(),
1234 array('categories' => $categories));
2f951d86 1235
3ec163dd
EL
1236 $transaction = $DB->start_delegated_transaction();
1237
1238 $createdcategories = array();
2f951d86 1239 foreach ($params['categories'] as $category) {
3ec163dd
EL
1240 if ($category['parent']) {
1241 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1242 throw new moodle_exception('unknowcategory');
1243 }
1244 $context = context_coursecat::instance($category['parent']);
1245 } else {
1246 $context = context_system::instance();
2f951d86 1247 }
2f951d86 1248 self::validate_context($context);
3ec163dd 1249 require_capability('moodle/category:manage', $context);
2f951d86 1250
3ec163dd
EL
1251 // Check name.
1252 if (textlib::strlen($category['name'])>255) {
1253 throw new moodle_exception('categorytoolong');
1254 }
2f951d86 1255
3ec163dd
EL
1256 $newcategory = new stdClass();
1257 $newcategory->name = $category['name'];
1258 $newcategory->parent = $category['parent'];
3ec163dd
EL
1259 $newcategory->sortorder = 999; // Same as in the course/editcategory.php .
1260 // Format the description.
1261 if (!empty($category['description'])) {
1262 $newcategory->description = $category['description'];
1263 }
93ce0e82 1264 $newcategory->descriptionformat = external_validate_format($category['descriptionformat']);
3ec163dd
EL
1265 if (isset($category['theme']) and !empty($CFG->allowcategorythemes)) {
1266 $newcategory->theme = $category['theme'];
2f951d86 1267 }
9615b623
JM
1268 // Check id number.
1269 if (!empty($category['idnumber'])) { // Same as in course/editcategory_form.php .
1270 if (textlib::strlen($category['idnumber'])>100) {
1271 throw new moodle_exception('idnumbertoolong');
1272 }
1273 if ($existing = $DB->get_record('course_categories', array('idnumber' => $category['idnumber']))) {
1274 if ($existing->id) {
1275 throw new moodle_exception('idnumbertaken');
1276 }
1277 }
1278 $newcategory->idnumber = $category['idnumber'];
1279 }
3ec163dd
EL
1280
1281 $newcategory = create_course_category($newcategory);
1282 // Populate special fields.
1283 fix_course_sortorder();
1284
1285 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
2f951d86
FS
1286 }
1287
3ec163dd
EL
1288 $transaction->allow_commit();
1289
1290 return $createdcategories;
2f951d86
FS
1291 }
1292
1293 /**
1294 * Returns description of method parameters
3ec163dd 1295 *
2f951d86
FS
1296 * @return external_function_parameters
1297 * @since Moodle 2.3
1298 */
3ec163dd
EL
1299 public static function create_categories_returns() {
1300 return new external_multiple_structure(
1301 new external_single_structure(
1302 array(
1303 'id' => new external_value(PARAM_INT, 'new category id'),
1304 'name' => new external_value(PARAM_TEXT, 'new category name'),
1305 )
1306 )
1307 );
2f951d86 1308 }
f2229c68
FS
1309
1310 /**
1311 * Returns description of method parameters
3ec163dd 1312 *
f2229c68
FS
1313 * @return external_function_parameters
1314 * @since Moodle 2.3
1315 */
1316 public static function update_categories_parameters() {
1317 return new external_function_parameters(
1318 array(
1319 'categories' => new external_multiple_structure(
1320 new external_single_structure(
1321 array(
1322 'id' => new external_value(PARAM_INT, 'course id'),
1323 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1324 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1325 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1326 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
93ce0e82 1327 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
f2229c68
FS
1328 'theme' => new external_value(PARAM_THEME,
1329 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1330 )
1331 )
1332 )
1333 )
1334 );
1335 }
1336
1337 /**
1338 * Update categories
3ec163dd 1339 *
f2229c68
FS
1340 * @param array $categories The list of categories to update
1341 * @return null
1342 * @since Moodle 2.3
1343 */
1344 public static function update_categories($categories) {
1345 global $CFG, $DB;
1346 require_once($CFG->dirroot . "/course/lib.php");
1347
1348 // Validate parameters.
1349 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1350
1351 $transaction = $DB->start_delegated_transaction();
1352
1353 foreach ($params['categories'] as $cat) {
1354 if (!$category = $DB->get_record('course_categories', array('id' => $cat['id']))) {
1355 throw new moodle_exception('unknowcategory');
1356 }
1357
1358 $categorycontext = context_coursecat::instance($cat['id']);
1359 self::validate_context($categorycontext);
1360 require_capability('moodle/category:manage', $categorycontext);
1361
1362 if (!empty($cat['name'])) {
d7238d08 1363 if (textlib::strlen($cat['name'])>255) {
f2229c68
FS
1364 throw new moodle_exception('categorytoolong');
1365 }
1366 $category->name = $cat['name'];
1367 }
1368 if (!empty($cat['idnumber'])) {
1369 if (textlib::strlen($cat['idnumber'])>100) {
1370 throw new moodle_exception('idnumbertoolong');
1371 }
1372 $category->idnumber = $cat['idnumber'];
1373 }
1374 if (!empty($cat['description'])) {
1375 $category->description = $cat['description'];
93ce0e82 1376 $category->descriptionformat = external_validate_format($cat['descriptionformat']);
f2229c68
FS
1377 }
1378 if (!empty($cat['theme'])) {
1379 $category->theme = $cat['theme'];
1380 }
1381 if (!empty($cat['parent']) && ($category->parent != $cat['parent'])) {
1382 // First check if parent exists.
1383 if (!$parent_cat = $DB->get_record('course_categories', array('id' => $cat['parent']))) {
1384 throw new moodle_exception('unknowcategory');
1385 }
1386 // Then check if we have capability.
1387 self::validate_context(get_category_or_system_context((int)$cat['parent']));
1388 require_capability('moodle/category:manage', get_category_or_system_context((int)$cat['parent']));
1389 // Finally move the category.
1390 move_category($category, $parent_cat);
1391 $category->parent = $cat['parent'];
fe77a34e
JM
1392 // Get updated path by move_category().
1393 $category->path = $DB->get_field('course_categories', 'path',
1394 array('id' => $category->id));
f2229c68
FS
1395 }
1396 $DB->update_record('course_categories', $category);
1397 }
1398
1399 $transaction->allow_commit();
1400 }
1401
1402 /**
1403 * Returns description of method result value
3ec163dd 1404 *
f2229c68 1405 * @return external_description
3ec163dd 1406 * @since Moodle 2.3
f2229c68
FS
1407 */
1408 public static function update_categories_returns() {
1409 return null;
1410 }
3ec163dd
EL
1411
1412 /**
1413 * Returns description of method parameters
1414 *
1415 * @return external_function_parameters
1416 * @since Moodle 2.3
1417 */
1418 public static function delete_categories_parameters() {
1419 return new external_function_parameters(
1420 array(
1421 'categories' => new external_multiple_structure(
1422 new external_single_structure(
1423 array(
1424 'id' => new external_value(PARAM_INT, 'category id to delete'),
1425 'newparent' => new external_value(PARAM_INT,
1426 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
1427 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
1428 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
1429 )
1430 )
1431 )
1432 )
1433 );
1434 }
1435
1436 /**
1437 * Delete categories
1438 *
1439 * @param array $categories A list of category ids
1440 * @return array
1441 * @since Moodle 2.3
1442 */
1443 public static function delete_categories($categories) {
1444 global $CFG, $DB;
1445 require_once($CFG->dirroot . "/course/lib.php");
1446
1447 // Validate parameters.
1448 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1449
f823158b
EL
1450 $transaction = $DB->start_delegated_transaction();
1451
3ec163dd
EL
1452 foreach ($params['categories'] as $category) {
1453 if (!$deletecat = $DB->get_record('course_categories', array('id' => $category['id']))) {
1454 throw new moodle_exception('unknowcategory');
1455 }
1456 $context = context_coursecat::instance($deletecat->id);
1457 require_capability('moodle/category:manage', $context);
1458 self::validate_context($context);
1459 self::validate_context(get_category_or_system_context($deletecat->parent));
1460
1461 if ($category['recursive']) {
1462 // If recursive was specified, then we recursively delete the category's contents.
1463 category_delete_full($deletecat, false);
1464 } else {
1465 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1466 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1467 // We must move to an existing category.
1468 if (!empty($category['newparent'])) {
1469 if (!$DB->record_exists('course_categories', array('id' => $category['newparent']))) {
1470 throw new moodle_exception('unknowcategory');
1471 }
1472 $newparent = $category['newparent'];
1473 } else {
1474 $newparent = $deletecat->parent;
1475 }
1476
1477 // This operation is not allowed. We must move contents to an existing category.
1478 if ($newparent == 0) {
1479 throw new moodle_exception('movecatcontentstoroot');
1480 }
1481
1482 $parentcontext = get_category_or_system_context($newparent);
1483 require_capability('moodle/category:manage', $parentcontext);
1484 self::validate_context($parentcontext);
1485 category_delete_move($deletecat, $newparent, false);
1486 }
1487 }
1488
f823158b 1489 $transaction->allow_commit();
3ec163dd
EL
1490 }
1491
1492 /**
1493 * Returns description of method parameters
1494 *
1495 * @return external_function_parameters
1496 * @since Moodle 2.3
1497 */
1498 public static function delete_categories_returns() {
1499 return null;
1500 }
1501
5d1017e1
JM
1502}
1503
1504/**
4615817d
JM
1505 * Deprecated course external functions
1506 *
1507 * @package core_course
1508 * @copyright 2009 Petr Skodak
1509 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1510 * @since Moodle 2.0
1511 * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
1512 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1513 * @see core_course_external
5d1017e1
JM
1514 */
1515class moodle_course_external extends external_api {
1516
1517 /**
1518 * Returns description of method parameters
4615817d 1519 *
5d1017e1 1520 * @return external_function_parameters
4615817d
JM
1521 * @since Moodle 2.0
1522 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1523 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1524 * @see core_course_external::get_courses_parameters()
5d1017e1
JM
1525 */
1526 public static function get_courses_parameters() {
1527 return core_course_external::get_courses_parameters();
1528 }
1529
1530 /**
1531 * Get courses
4615817d 1532 *
5d1017e1 1533 * @param array $options
5d1017e1 1534 * @return array
4615817d
JM
1535 * @since Moodle 2.0
1536 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1537 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1538 * @see core_course_external::get_courses()
5d1017e1
JM
1539 */
1540 public static function get_courses($options) {
1541 return core_course_external::get_courses($options);
1542 }
1543
1544 /**
1545 * Returns description of method result value
4615817d 1546 *
5d1017e1 1547 * @return external_description
4615817d
JM
1548 * @since Moodle 2.0
1549 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1550 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1551 * @see core_course_external::get_courses_returns()
5d1017e1
JM
1552 */
1553 public static function get_courses_returns() {
1554 return core_course_external::get_courses_returns();
1555 }
1556
1557 /**
1558 * Returns description of method parameters
4615817d 1559 *
5d1017e1 1560 * @return external_function_parameters
4615817d
JM
1561 * @since Moodle 2.0
1562 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1563 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1564 * @see core_course_external::create_courses_parameters()
5d1017e1
JM
1565 */
1566 public static function create_courses_parameters() {
1567 return core_course_external::create_courses_parameters();
1568 }
1569
1570 /**
1571 * Create courses
4615817d 1572 *
5d1017e1
JM
1573 * @param array $courses
1574 * @return array courses (id and shortname only)
4615817d
JM
1575 * @since Moodle 2.0
1576 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1577 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1578 * @see core_course_external::create_courses()
5d1017e1
JM
1579 */
1580 public static function create_courses($courses) {
1581 return core_course_external::create_courses($courses);
1582 }
1583
1584 /**
1585 * Returns description of method result value
4615817d 1586 *
5d1017e1 1587 * @return external_description
4615817d
JM
1588 * @since Moodle 2.0
1589 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1590 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1591 * @see core_course_external::create_courses_returns()
5d1017e1
JM
1592 */
1593 public static function create_courses_returns() {
1594 return core_course_external::create_courses_returns();
1595 }
1596
ec0d6ea2 1597}