2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
21 * @package core_course
23 * @copyright 2009 Petr Skodak
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') ||
die;
29 require_once("$CFG->libdir/externallib.php");
32 * Course external functions
34 * @package core_course
36 * @copyright 2011 Jerome Mouneyrac
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 class core_course_external
extends external_api
{
43 * Returns description of method parameters
45 * @return external_function_parameters
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.')
56 ), 'Options, not used yet, might be used in later version', VALUE_DEFAULT
, array())
64 * @param int $courseid course id
65 * @param array $options These options are not used yet, might be used in later version
69 public static function get_course_contents($courseid, $options = array()) {
71 require_once($CFG->dirroot
. "/course/lib.php");
74 $params = self
::validate_parameters(self
::get_course_contents_parameters(),
75 array('courseid' => $courseid, 'options' => $options));
78 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST
);
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
));
84 require_once($CFG->dirroot
. '/course/format/' . $course->format
. '/lib.php');
87 // now security checks
88 $context = context_course
::instance($course->id
, IGNORE_MISSING
);
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);
98 $canupdatecourse = has_capability('moodle/course:update', $context);
100 //create return value
101 $coursecontents = array();
103 if ($canupdatecourse or $course->visible
104 or has_capability('moodle/course:viewhiddencourses', $context)) {
107 $modinfo = get_fast_modinfo($course);
108 $sections = $modinfo->get_section_info_all();
110 //for each sections (first displayed to last displayed)
111 $modinfosections = $modinfo->get_sections();
112 foreach ($sections as $key => $section) {
114 if (!$section->uservisible
) {
118 // reset $sectioncontents
119 $sectionvalues = array();
120 $sectionvalues['id'] = $section->id
;
121 $sectionvalues['name'] = get_section_name($course, $section);
122 $sectionvalues['visible'] = $section->visible
;
123 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
124 external_format_text($section->summary
, $section->summaryformat
,
125 $context->id
, 'course', 'section', $section->id
);
126 $sectioncontents = array();
128 //for each module of the section
129 if (!empty($modinfosections[$section->section
])) {
130 foreach ($modinfosections[$section->section
] as $cmid) {
131 $cm = $modinfo->cms
[$cmid];
133 // stop here if the module is not visible to the user
134 if (!$cm->uservisible
) {
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
;
148 $modcontext = context_module
::instance($cm->id
);
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
);
157 $url = $cm->get_url();
158 if ($url) { //labels don't have url
159 $module['url'] = $cm->get_url()->out(false);
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
;
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
;
173 $baseurl = 'webservice/pluginfile.php';
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;
185 //assign result to $sectioncontents
186 $sectioncontents[] = $module;
190 $sectionvalues['modules'] = $sectioncontents;
192 // assign result to $coursecontents
193 $coursecontents[] = $sectionvalues;
196 return $coursecontents;
200 * Returns description of method result value
202 * @return external_description
205 public static function get_course_contents_returns() {
206 return new external_multiple_structure(
207 new external_single_structure(
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'),
213 'summaryformat' => new external_format_value('summary'),
214 'modules' => new external_multiple_structure(
215 new external_single_structure(
217 'id' => new external_value(PARAM_INT
, 'activity id'),
218 'url' => new external_value(PARAM_URL
, 'activity url', VALUE_OPTIONAL
),
219 'name' => new external_value(PARAM_RAW
, 'activity module name'),
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(
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'),
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'),
247 ), VALUE_DEFAULT
, array()
258 * Returns description of method parameters
260 * @return external_function_parameters
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.',
271 ), 'options - operator OR is used', VALUE_DEFAULT
, array())
279 * @param array $options It contains an array (list of ids)
283 public static function get_courses($options = array()) {
285 require_once($CFG->dirroot
. "/course/lib.php");
288 $params = self
::validate_parameters(self
::get_courses_parameters(),
289 array('options' => $options));
292 if (!array_key_exists('ids', $params['options'])
293 or empty($params['options']['ids'])) {
294 $courses = $DB->get_records('course');
296 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
299 //create return value
300 $coursesinfo = array();
301 foreach ($courses as $course) {
303 // now security checks
304 $context = context_course
::instance($course->id
, IGNORE_MISSING
);
305 $courseformatoptions = course_get_format($course)->get_format_options();
307 self
::validate_context($context);
308 } catch (Exception
$e) {
309 $exceptionparam = new stdClass();
310 $exceptionparam->message
= $e->getMessage();
311 $exceptionparam->courseid
= $course->id
;
312 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
314 require_capability('moodle/course:view', $context);
316 $courseinfo = array();
317 $courseinfo['id'] = $course->id
;
318 $courseinfo['fullname'] = $course->fullname
;
319 $courseinfo['shortname'] = $course->shortname
;
320 $courseinfo['categoryid'] = $course->category
;
321 list($courseinfo['summary'], $courseinfo['summaryformat']) =
322 external_format_text($course->summary
, $course->summaryformat
, $context->id
, 'course', 'summary', 0);
323 $courseinfo['format'] = $course->format
;
324 $courseinfo['startdate'] = $course->startdate
;
325 if (array_key_exists('numsections', $courseformatoptions)) {
326 // For backward-compartibility
327 $courseinfo['numsections'] = $courseformatoptions['numsections'];
330 //some field should be returned only if the user has update permission
331 $courseadmin = has_capability('moodle/course:update', $context);
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
;
340 if (array_key_exists('hiddensections', $courseformatoptions)) {
341 // For backward-compartibility
342 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
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
;
352 $courseinfo['completionnotify'] = $course->completionnotify
;
353 $courseinfo['courseformatoptions'] = array();
354 foreach ($courseformatoptions as $key => $value) {
355 $courseinfo['courseformatoptions'][] = array(
362 if ($courseadmin or $course->visible
363 or has_capability('moodle/course:viewhiddencourses', $context)) {
364 $coursesinfo[] = $courseinfo;
372 * Returns description of method result value
374 * @return external_description
377 public static function get_courses_returns() {
378 return new external_multiple_structure(
379 new external_single_structure(
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'),
389 'summaryformat' => new external_format_value('summary'),
390 'format' => new external_value(PARAM_PLUGIN
,
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'),
398 'numsections' => new external_value(PARAM_INT
,
399 '(deprecated, use courseformatoptions) number of weeks/topics',
401 'maxbytes' => new external_value(PARAM_INT
,
402 'largest size of file that can be uploaded into the course',
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
,
409 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
411 'groupmode' => new external_value(PARAM_INT
, 'no group, separate, visible',
413 'groupmodeforce' => new external_value(PARAM_INT
, '1: yes, 0: no',
415 'defaultgroupingid' => new external_value(PARAM_INT
, 'default grouping id',
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.',
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
),
431 'courseformatoptions' => new external_multiple_structure(
432 new external_single_structure(
433 array('name' => new external_value(PARAM_ALPHANUMEXT
, 'course format option name'),
434 'value' => new external_value(PARAM_RAW
, 'course format option value')
436 'additional options for particular course format', VALUE_OPTIONAL
444 * Returns description of method parameters
446 * @return external_function_parameters
449 public static function create_courses_parameters() {
450 $courseconfig = get_config('moodlecourse'); //needed for many default values
451 return new external_function_parameters(
453 'courses' => new external_multiple_structure(
454 new external_single_structure(
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
),
461 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT
),
462 'format' => new external_value(PARAM_PLUGIN
,
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
),
473 'numsections' => new external_value(PARAM_INT
,
474 '(deprecated, use courseformatoptions) number of weeks/topics',
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
,
485 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
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',
493 'enablecompletion' => new external_value(PARAM_INT
,
494 'Enabled, control via completion and activity settings. Disabled,
495 not shown in activity settings.',
497 'completionnotify' => new external_value(PARAM_INT
,
498 '1: yes 0: no', VALUE_OPTIONAL
),
499 'lang' => new external_value(PARAM_SAFEDIR
,
500 'forced course language', VALUE_OPTIONAL
),
501 'forcetheme' => new external_value(PARAM_PLUGIN
,
502 'name of the force theme', VALUE_OPTIONAL
),
503 'courseformatoptions' => new external_multiple_structure(
504 new external_single_structure(
505 array('name' => new external_value(PARAM_ALPHANUMEXT
, 'course format option name'),
506 'value' => new external_value(PARAM_RAW
, 'course format option value')
508 'additional options for particular course format', VALUE_OPTIONAL
),
510 ), 'courses to create'
519 * @param array $courses
520 * @return array courses (id and shortname only)
523 public static function create_courses($courses) {
525 require_once($CFG->dirroot
. "/course/lib.php");
526 require_once($CFG->libdir
. '/completionlib.php');
528 $params = self
::validate_parameters(self
::create_courses_parameters(),
529 array('courses' => $courses));
531 $availablethemes = get_plugin_list('theme');
532 $availablelangs = get_string_manager()->get_list_of_translations();
534 $transaction = $DB->start_delegated_transaction();
536 foreach ($params['courses'] as $course) {
538 // Ensure the current user is allowed to run this function
539 $context = context_coursecat
::instance($course['categoryid'], IGNORE_MISSING
);
541 self
::validate_context($context);
542 } catch (Exception
$e) {
543 $exceptionparam = new stdClass();
544 $exceptionparam->message
= $e->getMessage();
545 $exceptionparam->catid
= $course['categoryid'];
546 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
548 require_capability('moodle/course:create', $context);
550 // Make sure lang is valid
551 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
552 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
555 // Make sure theme is valid
556 if (array_key_exists('forcetheme', $course)) {
557 if (!empty($CFG->allowcoursethemes
)) {
558 if (empty($availablethemes[$course['forcetheme']])) {
559 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
561 $course['theme'] = $course['forcetheme'];
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
;
572 //set default value for completion
573 $courseconfig = get_config('moodlecourse');
574 if (completion_info
::is_enabled_for_site()) {
575 if (!array_key_exists('enablecompletion', $course)) {
576 $course['enablecompletion'] = $courseconfig->enablecompletion
;
579 $course['enablecompletion'] = 0;
582 $course['category'] = $course['categoryid'];
585 $course['summaryformat'] = external_validate_format($course['summaryformat']);
587 if (!empty($course['courseformatoptions'])) {
588 foreach ($course['courseformatoptions'] as $option) {
589 $course[$option['name']] = $option['value'];
593 //Note: create_course() core function check shortname, idnumber, category
594 $course['id'] = create_course((object) $course)->id
;
596 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
599 $transaction->allow_commit();
601 return $resultcourses;
605 * Returns description of method result value
607 * @return external_description
610 public static function create_courses_returns() {
611 return new external_multiple_structure(
612 new external_single_structure(
614 'id' => new external_value(PARAM_INT
, 'course id'),
615 'shortname' => new external_value(PARAM_TEXT
, 'short name'),
624 * @return external_function_parameters
627 public static function update_courses_parameters() {
628 return new external_function_parameters(
630 'courses' => new external_multiple_structure(
631 new external_single_structure(
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
,
657 '(deprecated, use courseformatoptions) How the hidden sections in the course are
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
),
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')
673 'additional options for particular course format', VALUE_OPTIONAL
),
675 ), 'courses to update'
684 * @param array $courses
687 public static function update_courses($courses) {
689 require_once($CFG->dirroot
. "/course/lib.php");
692 $params = self
::validate_parameters(self
::update_courses_parameters(),
693 array('courses' => $courses));
695 $availablethemes = get_plugin_list('theme');
696 $availablelangs = get_string_manager()->get_list_of_translations();
698 foreach ($params['courses'] as $course) {
699 // Catch any exception while updating course and return as warning to user.
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);
705 $oldcourse = course_get_format($course['id'])->get_course();
707 require_capability('moodle/course:update', $context);
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'];
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);
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');
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');
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);
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']);
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);
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');
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');
763 $course['theme'] = $course['forcetheme'];
768 // Make sure completion is enabled before setting it.
769 if (array_key_exists('enabledcompletion', $course) && !completion_info
::is_enabled_for_site()) {
770 $course['enabledcompletion'] = 0;
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']);
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'];
786 // Update course if user has all required capabilities.
787 update_course((object) $course);
788 } catch (Exception
$e) {
790 $warning['item'] = 'course';
791 $warning['itemid'] = $course['id'];
792 if ($e instanceof moodle_exception
) {
793 $warning['warningcode'] = $e->errorcode
;
795 $warning['warningcode'] = $e->getCode();
797 $warning['message'] = $e->getMessage();
798 $warnings[] = $warning;
803 $result['warnings'] = $warnings;
808 * Returns description of method result value
810 * @return external_description
813 public static function update_courses_returns() {
814 return new external_single_structure(
816 'warnings' => new external_warnings()
822 * Returns description of method parameters
824 * @return external_function_parameters
827 public static function delete_courses_parameters() {
828 return new external_function_parameters(
830 'courseids' => new external_multiple_structure(new external_value(PARAM_INT
, 'course ID')),
838 * @param array $courseids A list of course ids
841 public static function delete_courses($courseids) {
843 require_once($CFG->dirroot
."/course/lib.php");
845 // Parameter validation.
846 $params = self
::validate_parameters(self
::delete_courses_parameters(), array('courseids'=>$courseids));
848 $transaction = $DB->start_delegated_transaction();
850 foreach ($params['courseids'] as $courseid) {
851 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST
);
853 // Check if the context is valid.
854 $coursecontext = context_course
::instance($course->id
);
855 self
::validate_context($coursecontext);
857 // Check if the current user has enought permissions.
858 if (!can_delete_course($courseid)) {
859 throw new moodle_exception('cannotdeletecategorycourse', 'error',
860 '', format_string($course->fullname
)." (id: $courseid)");
863 delete_course($course, false);
866 $transaction->allow_commit();
872 * Returns description of method result value
874 * @return external_description
877 public static function delete_courses_returns() {
882 * Returns description of method parameters
884 * @return external_function_parameters
887 public static function duplicate_course_parameters() {
888 return new external_function_parameters(
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(
898 'name' => new external_value(PARAM_ALPHAEXT
, 'The backup option name:
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),
904 "comments" (int) Include user comments (default to 0 that is equal to no),
905 "userscompletion" (int) Include user course completion information (default to 0 that is equal to no),
906 "logs" (int) Include course logs (default to 0 that is equal to no),
907 "grade_histories" (int) Include histories (default to 0 that is equal to no)'
909 'value' => new external_value(PARAM_RAW
, 'the value for the option 1 (yes) or 0 (no)'
912 ), VALUE_DEFAULT
, array()
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
930 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
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');
935 // Parameter validation.
936 $params = self
::validate_parameters(
937 self
::duplicate_course_parameters(),
939 'courseid' => $courseid,
940 'fullname' => $fullname,
941 'shortname' => $shortname,
942 'categoryid' => $categoryid,
943 'visible' => $visible,
944 'options' => $options
948 // Context validation.
950 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
951 throw new moodle_exception('invalidcourseid', 'error');
954 // Category where duplicated course is going to be created.
955 $categorycontext = context_coursecat
::instance($params['categoryid']);
956 self
::validate_context($categorycontext);
958 // Course to be duplicated.
959 $coursecontext = context_course
::instance($course->id
);
960 self
::validate_context($coursecontext);
962 $backupdefaults = array(
967 'role_assignments' => 0,
969 'userscompletion' => 0,
971 'grade_histories' => 0
974 $backupsettings = array();
975 // Check for backup and restore options.
976 if (!empty($params['options'])) {
977 foreach ($params['options'] as $option) {
979 // Strict check for a correct value (allways 1 or 0, true or false).
980 $value = clean_param($option['value'], PARAM_INT
);
982 if ($value !== 0 and $value !== 1) {
983 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
986 if (!isset($backupdefaults[$option['name']])) {
987 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
990 $backupsettings[$option['name']] = $value;
994 // Capability checking.
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);
1001 if (!empty($backupsettings['users'])) {
1002 require_capability('moodle/backup:userinfo', $coursecontext);
1003 require_capability('moodle/restore:userinfo', $categorycontext);
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
;
1012 $foundcoursenamestring = implode(',', $foundcoursenames);
1013 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1016 // Backup the course.
1018 $bc = new backup_controller(backup
::TYPE_1COURSE
, $course->id
, backup
::FORMAT_MOODLE
,
1019 backup
::INTERACTIVE_NO
, backup
::MODE_SAMESITE
, $USER->id
);
1021 foreach ($backupsettings as $name => $value) {
1022 $bc->get_plan()->get_setting($name)->set_value($value);
1025 $backupid = $bc->get_backupid();
1026 $backupbasepath = $bc->get_plan()->get_basepath();
1028 $bc->execute_plan();
1029 $results = $bc->get_results();
1030 $file = $results['backup_destination'];
1034 // Restore the backup immediately.
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);
1041 // Create new course.
1042 $newcourseid = restore_dbops
::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1044 $rc = new restore_controller($backupid, $newcourseid,
1045 backup
::INTERACTIVE_NO
, backup
::MODE_SAMESITE
, $USER->id
, backup
::TARGET_NEW_COURSE
);
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);
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);
1063 foreach ($precheckresults['errors'] as $error) {
1064 $errorinfo .= $error;
1067 if (array_key_exists('warnings', $precheckresults)) {
1068 foreach ($precheckresults['warnings'] as $warning) {
1069 $errorinfo .= $warning;
1073 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1077 $rc->execute_plan();
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'];
1085 // Set shortname and fullname back.
1086 $DB->update_record('course', $course);
1088 if (empty($CFG->keeptempdirectoriesonbackup
)) {
1089 fulldelete($backupbasepath);
1092 // Delete the course backup file created by this WebService. Originally located in the course backups area.
1095 return array('id' => $course->id
, 'shortname' => $course->shortname
);
1099 * Returns description of method result value
1101 * @return external_description
1104 public static function duplicate_course_returns() {
1105 return new external_single_structure(
1107 'id' => new external_value(PARAM_INT
, 'course id'),
1108 'shortname' => new external_value(PARAM_TEXT
, 'short name'),
1114 * Returns description of method parameters for import_course
1116 * @return external_function_parameters
1119 public static function import_course_parameters() {
1120 return new external_function_parameters(
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(
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)'
1133 'value' => new external_value(PARAM_RAW
, 'the value for the option 1 (yes) or 0 (no)'
1136 ), VALUE_DEFAULT
, array()
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
1152 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
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');
1157 // Parameter validation.
1158 $params = self
::validate_parameters(
1159 self
::import_course_parameters(),
1161 'importfrom' => $importfrom,
1162 'importto' => $importto,
1163 'deletecontent' => $deletecontent,
1164 'options' => $options
1168 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1169 throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
1172 // Context validation.
1174 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
1175 throw new moodle_exception('invalidcourseid', 'error');
1178 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
1179 throw new moodle_exception('invalidcourseid', 'error');
1182 $importfromcontext = context_course
::instance($importfrom->id
);
1183 self
::validate_context($importfromcontext);
1185 $importtocontext = context_course
::instance($importto->id
);
1186 self
::validate_context($importtocontext);
1188 $backupdefaults = array(
1194 $backupsettings = array();
1196 // Check for backup and restore options.
1197 if (!empty($params['options'])) {
1198 foreach ($params['options'] as $option) {
1200 // Strict check for a correct value (allways 1 or 0, true or false).
1201 $value = clean_param($option['value'], PARAM_INT
);
1203 if ($value !== 0 and $value !== 1) {
1204 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1207 if (!isset($backupdefaults[$option['name']])) {
1208 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1211 $backupsettings[$option['name']] = $value;
1215 // Capability checking.
1217 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1218 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1220 $bc = new backup_controller(backup
::TYPE_1COURSE
, $importfrom->id
, backup
::FORMAT_MOODLE
,
1221 backup
::INTERACTIVE_NO
, backup
::MODE_IMPORT
, $USER->id
);
1223 foreach ($backupsettings as $name => $value) {
1224 $bc->get_plan()->get_setting($name)->set_value($value);
1227 $backupid = $bc->get_backupid();
1228 $backupbasepath = $bc->get_plan()->get_basepath();
1230 $bc->execute_plan();
1233 // Restore the backup immediately.
1235 // Check if we must delete the contents of the destination course.
1236 if ($params['deletecontent']) {
1237 $restoretarget = backup
::TARGET_EXISTING_DELETING
;
1239 $restoretarget = backup
::TARGET_EXISTING_ADDING
;
1242 $rc = new restore_controller($backupid, $importto->id
,
1243 backup
::INTERACTIVE_NO
, backup
::MODE_IMPORT
, $USER->id
, $restoretarget);
1245 foreach ($backupsettings as $name => $value) {
1246 $rc->get_plan()->get_setting($name)->set_value($value);
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);
1258 foreach ($precheckresults['errors'] as $error) {
1259 $errorinfo .= $error;
1262 if (array_key_exists('warnings', $precheckresults)) {
1263 foreach ($precheckresults['warnings'] as $warning) {
1264 $errorinfo .= $warning;
1268 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1271 if ($restoretarget == backup
::TARGET_EXISTING_DELETING
) {
1272 restore_dbops
::delete_course_content($importto->id
);
1276 $rc->execute_plan();
1279 if (empty($CFG->keeptempdirectoriesonbackup
)) {
1280 fulldelete($backupbasepath);
1287 * Returns description of method result value
1289 * @return external_description
1292 public static function import_course_returns() {
1297 * Returns description of method parameters
1299 * @return external_function_parameters
1302 public static function get_categories_parameters() {
1303 return new external_function_parameters(
1305 'criteria' => new external_multiple_structure(
1306 new external_single_structure(
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,'.
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.'.
1317 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1318 '"theme" (string) only return the categories having this theme'.
1319 ' - user must have \'moodle/category:manage\' to search on theme'),
1320 'value' => new external_value(PARAM_RAW
, 'the value to match')
1322 ), 'criteria', VALUE_DEFAULT
, array()
1324 'addsubcategories' => new external_value(PARAM_BOOL
, 'return the sub categories infos
1325 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT
, 1)
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
1338 public static function get_categories($criteria = array(), $addsubcategories = true) {
1340 require_once($CFG->dirroot
. "/course/lib.php");
1342 // Validate parameters.
1343 $params = self
::validate_parameters(self
::get_categories_parameters(),
1344 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1346 // Retrieve the categories.
1347 $categories = array();
1348 if (!empty($params['criteria'])) {
1350 $conditions = array();
1352 foreach ($params['criteria'] as $crit) {
1353 $key = trim($crit['key']);
1355 // Trying to avoid duplicate keys.
1356 if (!isset($conditions[$key])) {
1358 $context = context_system
::instance();
1362 $value = clean_param($crit['value'], PARAM_INT
);
1366 if (has_capability('moodle/category:manage', $context)) {
1367 $value = clean_param($crit['value'], PARAM_RAW
);
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.');
1378 $value = clean_param($crit['value'], PARAM_TEXT
);
1382 $value = clean_param($crit['value'], PARAM_INT
);
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
);
1391 throw new moodle_exception('criteriaerror',
1392 'webservice', '', null,
1393 'You don\'t have the permissions to search on the "visible" field.');
1398 if (has_capability('moodle/category:manage', $context)) {
1399 $value = clean_param($crit['value'], PARAM_THEME
);
1401 throw new moodle_exception('criteriaerror',
1402 'webservice', '', null,
1403 'You don\'t have the permissions to search on the "theme" field.');
1408 throw new moodle_exception('criteriaerror',
1409 'webservice', '', null,
1410 'You can not search on this criteria: ' . $key);
1413 if (isset($value)) {
1414 $conditions[$key] = $crit['value'];
1415 $wheres[] = $key . " = :" . $key;
1420 if (!empty($wheres)) {
1421 $wheres = implode(" AND ", $wheres);
1423 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1425 // Retrieve its sub subcategories (all levels).
1426 if ($categories and !empty($params['addsubcategories'])) {
1427 $newcategories = array();
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'];
1436 if (isset($conditions['theme'])) {
1437 $additionalselect .= ' AND theme= :theme';
1438 $additionalparams['theme'] = $conditions['theme'];
1441 foreach ($categories as $category) {
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);
1445 $newcategories = $newcategories +
$subcategories; // Both arrays have integer as keys.
1447 $categories = $categories +
$newcategories;
1452 // Retrieve all categories in the database.
1453 $categories = $DB->get_records('course_categories');
1456 // The not returned categories. key => category id, value => reason of exclusion.
1457 $excludedcats = array();
1459 // The returned categories.
1460 $categoriesinfo = array();
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");
1466 foreach ($categories as $category) {
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';
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';
1485 // Check the user can use the category context.
1486 $context = context_coursecat
::instance($category->id
);
1488 self
::validate_context($context);
1489 } catch (Exception
$e) {
1490 $excludedcats[$category->id
] = 'context';
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);
1501 // Return the category information.
1502 if (!isset($excludedcats[$category->id
])) {
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)) {
1509 $categoryinfo = array();
1510 $categoryinfo['id'] = $category->id
;
1511 $categoryinfo['name'] = $category->name
;
1512 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1513 external_format_text($category->description
, $category->descriptionformat
,
1514 $context->id
, 'coursecat', 'description', null);
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
;
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
;
1530 $categoriesinfo[] = $categoryinfo;
1532 $excludedcats[$category->id
] = 'visibility';
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");
1540 return $categoriesinfo;
1544 * Sort categories array by path
1545 * private function: only used by get_categories
1547 * @param array $category1
1548 * @param array $category2
1549 * @return int result of strcmp
1552 private static function compare_categories_by_path($category1, $category2) {
1553 return strcmp($category1->path
, $category2->path
);
1557 * Sort categories array by sortorder
1558 * private function: only used by get_categories
1560 * @param array $category1
1561 * @param array $category2
1562 * @return int result of strcmp
1565 private static function compare_categories_by_sortorder($category1, $category2) {
1566 return strcmp($category1['sortorder'], $category2['sortorder']);
1570 * Returns description of method result value
1572 * @return external_description
1575 public static function get_categories_returns() {
1576 return new external_multiple_structure(
1577 new external_single_structure(
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'),
1583 'descriptionformat' => new external_format_value('description'),
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'
1599 * Returns description of method parameters
1601 * @return external_function_parameters
1604 public static function create_categories_parameters() {
1605 return new external_function_parameters(
1607 'categories' => new external_multiple_structure(
1608 new external_single_structure(
1610 'name' => new external_value(PARAM_TEXT
, 'new category name'),
1611 'parent' => new external_value(PARAM_INT
,
1612 'the parent category id inside which the new category will be created
1613 - set to 0 for a root category',
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
),
1619 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT
),
1620 'theme' => new external_value(PARAM_THEME
,
1621 'the new category theme. This option must be enabled on moodle',
1633 * @param array $categories - see create_categories_parameters() for the array structure
1634 * @return array - see create_categories_returns() for the array structure
1637 public static function create_categories($categories) {
1639 require_once($CFG->libdir
. "/coursecatlib.php");
1641 $params = self
::validate_parameters(self
::create_categories_parameters(),
1642 array('categories' => $categories));
1644 $transaction = $DB->start_delegated_transaction();
1646 $createdcategories = array();
1647 foreach ($params['categories'] as $category) {
1648 if ($category['parent']) {
1649 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1650 throw new moodle_exception('unknowcategory');
1652 $context = context_coursecat
::instance($category['parent']);
1654 $context = context_system
::instance();
1656 self
::validate_context($context);
1657 require_capability('moodle/category:manage', $context);
1659 // this will validate format and throw an exception if there are errors
1660 external_validate_format($category['descriptionformat']);
1662 $newcategory = coursecat
::create($category);
1664 $createdcategories[] = array('id' => $newcategory->id
, 'name' => $newcategory->name
);
1667 $transaction->allow_commit();
1669 return $createdcategories;
1673 * Returns description of method parameters
1675 * @return external_function_parameters
1678 public static function create_categories_returns() {
1679 return new external_multiple_structure(
1680 new external_single_structure(
1682 'id' => new external_value(PARAM_INT
, 'new category id'),
1683 'name' => new external_value(PARAM_TEXT
, 'new category name'),
1690 * Returns description of method parameters
1692 * @return external_function_parameters
1695 public static function update_categories_parameters() {
1696 return new external_function_parameters(
1698 'categories' => new external_multiple_structure(
1699 new external_single_structure(
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
),
1706 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT
),
1707 'theme' => new external_value(PARAM_THEME
,
1708 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL
),
1719 * @param array $categories The list of categories to update
1723 public static function update_categories($categories) {
1725 require_once($CFG->libdir
. "/coursecatlib.php");
1727 // Validate parameters.
1728 $params = self
::validate_parameters(self
::update_categories_parameters(), array('categories' => $categories));
1730 $transaction = $DB->start_delegated_transaction();
1732 foreach ($params['categories'] as $cat) {
1733 $category = coursecat
::get($cat['id']);
1735 $categorycontext = context_coursecat
::instance($cat['id']);
1736 self
::validate_context($categorycontext);
1737 require_capability('moodle/category:manage', $categorycontext);
1739 // this will throw an exception if descriptionformat is not valid
1740 external_validate_format($cat['descriptionformat']);
1742 $category->update($cat);
1745 $transaction->allow_commit();
1749 * Returns description of method result value
1751 * @return external_description
1754 public static function update_categories_returns() {
1759 * Returns description of method parameters
1761 * @return external_function_parameters
1764 public static function delete_categories_parameters() {
1765 return new external_function_parameters(
1767 'categories' => new external_multiple_structure(
1768 new external_single_structure(
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)
1785 * @param array $categories A list of category ids
1789 public static function delete_categories($categories) {
1791 require_once($CFG->dirroot
. "/course/lib.php");
1792 require_once($CFG->libdir
. "/coursecatlib.php");
1794 // Validate parameters.
1795 $params = self
::validate_parameters(self
::delete_categories_parameters(), array('categories' => $categories));
1797 $transaction = $DB->start_delegated_transaction();
1799 foreach ($params['categories'] as $category) {
1800 $deletecat = coursecat
::get($category['id'], MUST_EXIST
);
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
));
1806 if ($category['recursive']) {
1807 // If recursive was specified, then we recursively delete the category's contents.
1808 if ($deletecat->can_delete_full()) {
1809 $deletecat->delete_full(false);
1811 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
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'])) {
1818 $newparentcat = coursecat
::get($category['newparent']);
1820 $newparentcat = coursecat
::get($deletecat->parent
);
1823 // This operation is not allowed. We must move contents to an existing category.
1824 if (!$newparentcat->id
) {
1825 throw new moodle_exception('movecatcontentstoroot');
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);
1832 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1837 $transaction->allow_commit();
1841 * Returns description of method parameters
1843 * @return external_function_parameters
1846 public static function delete_categories_returns() {
1851 * Describes the parameters for delete_modules.
1853 * @return external_external_function_parameters
1856 public static function delete_modules_parameters() {
1857 return new external_function_parameters (
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'),
1866 * Deletes a list of provided module instances.
1868 * @param array $cmids the course module ids
1871 public static function delete_modules($cmids) {
1874 // Require course file containing the course delete module function.
1875 require_once($CFG->dirroot
. "/course/lib.php");
1877 // Clean the parameters.
1878 $params = self
::validate_parameters(self
::delete_modules_parameters(), array('cmids' => $cmids));
1880 // Keep track of the course ids we have performed a capability check on to avoid repeating.
1881 $arrcourseschecked = array();
1883 foreach ($params['cmids'] as $cmid) {
1884 // Get the course module.
1885 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST
);
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
;
1896 // Ensure they can delete this module.
1897 $modcontext = context_module
::instance($cm->id
);
1898 require_capability('moodle/course:manageactivities', $modcontext);
1900 // Delete the module.
1901 course_delete_module($cm->id
);
1906 * Describes the delete_modules return value.
1908 * @return external_single_structure
1911 public static function delete_modules_returns() {
1917 * Deprecated course external functions
1919 * @package core_course
1920 * @copyright 2009 Petr Skodak
1921 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1923 * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
1924 * @see core_course_external
1926 class moodle_course_external
extends external_api
{
1929 * Returns description of method parameters
1931 * @return external_function_parameters
1933 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1934 * @see core_course_external::get_courses_parameters()
1936 public static function get_courses_parameters() {
1937 return core_course_external
::get_courses_parameters();
1943 * @param array $options
1946 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1947 * @see core_course_external::get_courses()
1949 public static function get_courses($options) {
1950 return core_course_external
::get_courses($options);
1954 * Returns description of method result value
1956 * @return external_description
1958 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1959 * @see core_course_external::get_courses_returns()
1961 public static function get_courses_returns() {
1962 return core_course_external
::get_courses_returns();
1966 * Returns description of method parameters
1968 * @return external_function_parameters
1970 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1971 * @see core_course_external::create_courses_parameters()
1973 public static function create_courses_parameters() {
1974 return core_course_external
::create_courses_parameters();
1980 * @param array $courses
1981 * @return array courses (id and shortname only)
1983 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1984 * @see core_course_external::create_courses()
1986 public static function create_courses($courses) {
1987 return core_course_external
::create_courses($courses);
1991 * Returns description of method result value
1993 * @return external_description
1995 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1996 * @see core_course_external::create_courses_returns()
1998 public static function create_courses_returns() {
1999 return core_course_external
::create_courses_returns();