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 if ($course->id
!= SITEID
) {
81 // Check course format exist.
82 if (!file_exists($CFG->dirroot
. '/course/format/' . $course->format
. '/lib.php')) {
83 throw new moodle_exception('cannotgetcoursecontents', 'webservice', '', null,
84 get_string('courseformatnotfound', 'error', $course->format
));
86 require_once($CFG->dirroot
. '/course/format/' . $course->format
. '/lib.php');
90 // now security checks
91 $context = context_course
::instance($course->id
, IGNORE_MISSING
);
93 self
::validate_context($context);
94 } catch (Exception
$e) {
95 $exceptionparam = new stdClass();
96 $exceptionparam->message
= $e->getMessage();
97 $exceptionparam->courseid
= $course->id
;
98 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
101 $canupdatecourse = has_capability('moodle/course:update', $context);
103 //create return value
104 $coursecontents = array();
106 if ($canupdatecourse or $course->visible
107 or has_capability('moodle/course:viewhiddencourses', $context)) {
110 $modinfo = get_fast_modinfo($course);
111 $sections = $modinfo->get_section_info_all();
113 //for each sections (first displayed to last displayed)
114 $modinfosections = $modinfo->get_sections();
115 foreach ($sections as $key => $section) {
117 if (!$section->uservisible
) {
121 // reset $sectioncontents
122 $sectionvalues = array();
123 $sectionvalues['id'] = $section->id
;
124 $sectionvalues['name'] = get_section_name($course, $section);
125 $sectionvalues['visible'] = $section->visible
;
126 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
127 external_format_text($section->summary
, $section->summaryformat
,
128 $context->id
, 'course', 'section', $section->id
);
129 $sectioncontents = array();
131 //for each module of the section
132 if (!empty($modinfosections[$section->section
])) {
133 foreach ($modinfosections[$section->section
] as $cmid) {
134 $cm = $modinfo->cms
[$cmid];
136 // stop here if the module is not visible to the user
137 if (!$cm->uservisible
) {
143 //common info (for people being able to see the module or availability dates)
144 $module['id'] = $cm->id
;
145 $module['name'] = format_string($cm->name
, true);
146 $module['instance'] = $cm->instance
;
147 $module['modname'] = $cm->modname
;
148 $module['modplural'] = $cm->modplural
;
149 $module['modicon'] = $cm->get_icon_url()->out(false);
150 $module['indent'] = $cm->indent
;
152 $modcontext = context_module
::instance($cm->id
);
154 if (!empty($cm->showdescription
) or $cm->modname
== 'label') {
155 // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML.
156 list($module['description'], $descriptionformat) = external_format_text($cm->content
,
157 FORMAT_HTML
, $modcontext->id
, $cm->modname
, 'intro', $cm->id
);
162 if ($url) { //labels don't have url
163 $module['url'] = $url->out(false);
166 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
167 context_module
::instance($cm->id
));
168 //user that can view hidden module should know about the visibility
169 $module['visible'] = $cm->visible
;
171 // Availability date (also send to user who can see hidden module).
172 if ($CFG->enableavailability
&& ($canviewhidden ||
$canupdatecourse)) {
173 $module['availability'] = $cm->availability
;
176 $baseurl = 'webservice/pluginfile.php';
178 //call $modulename_export_contents
179 //(each module callback take care about checking the capabilities)
180 require_once($CFG->dirroot
. '/mod/' . $cm->modname
. '/lib.php');
181 $getcontentfunction = $cm->modname
.'_export_contents';
182 if (function_exists($getcontentfunction)) {
183 if ($contents = $getcontentfunction($cm, $baseurl)) {
184 $module['contents'] = $contents;
188 //assign result to $sectioncontents
189 $sectioncontents[] = $module;
193 $sectionvalues['modules'] = $sectioncontents;
195 // assign result to $coursecontents
196 $coursecontents[] = $sectionvalues;
199 return $coursecontents;
203 * Returns description of method result value
205 * @return external_description
208 public static function get_course_contents_returns() {
209 return new external_multiple_structure(
210 new external_single_structure(
212 'id' => new external_value(PARAM_INT
, 'Section ID'),
213 'name' => new external_value(PARAM_TEXT
, 'Section name'),
214 'visible' => new external_value(PARAM_INT
, 'is the section visible', VALUE_OPTIONAL
),
215 'summary' => new external_value(PARAM_RAW
, 'Section description'),
216 'summaryformat' => new external_format_value('summary'),
217 'modules' => new external_multiple_structure(
218 new external_single_structure(
220 'id' => new external_value(PARAM_INT
, 'activity id'),
221 'url' => new external_value(PARAM_URL
, 'activity url', VALUE_OPTIONAL
),
222 'name' => new external_value(PARAM_RAW
, 'activity module name'),
223 'instance' => new external_value(PARAM_INT
, 'instance id', VALUE_OPTIONAL
),
224 'description' => new external_value(PARAM_RAW
, 'activity description', VALUE_OPTIONAL
),
225 'visible' => new external_value(PARAM_INT
, 'is the module visible', VALUE_OPTIONAL
),
226 'modicon' => new external_value(PARAM_URL
, 'activity icon url'),
227 'modname' => new external_value(PARAM_PLUGIN
, 'activity module type'),
228 'modplural' => new external_value(PARAM_TEXT
, 'activity module plural name'),
229 'availability' => new external_value(PARAM_RAW
, 'module availability settings', VALUE_OPTIONAL
),
230 'indent' => new external_value(PARAM_INT
, 'number of identation in the site'),
231 'contents' => new external_multiple_structure(
232 new external_single_structure(
235 'type'=> new external_value(PARAM_TEXT
, 'a file or a folder or external link'),
236 'filename'=> new external_value(PARAM_FILE
, 'filename'),
237 'filepath'=> new external_value(PARAM_PATH
, 'filepath'),
238 'filesize'=> new external_value(PARAM_INT
, 'filesize'),
239 'fileurl' => new external_value(PARAM_URL
, 'downloadable file url', VALUE_OPTIONAL
),
240 'content' => new external_value(PARAM_RAW
, 'Raw content, will be used when type is content', VALUE_OPTIONAL
),
241 'timecreated' => new external_value(PARAM_INT
, 'Time created'),
242 'timemodified' => new external_value(PARAM_INT
, 'Time modified'),
243 'sortorder' => new external_value(PARAM_INT
, 'Content sort order'),
245 // copyright related info
246 'userid' => new external_value(PARAM_INT
, 'User who added this content to moodle'),
247 'author' => new external_value(PARAM_TEXT
, 'Content owner'),
248 'license' => new external_value(PARAM_TEXT
, 'Content license'),
250 ), VALUE_DEFAULT
, array()
261 * Returns description of method parameters
263 * @return external_function_parameters
266 public static function get_courses_parameters() {
267 return new external_function_parameters(
268 array('options' => new external_single_structure(
269 array('ids' => new external_multiple_structure(
270 new external_value(PARAM_INT
, 'Course id')
271 , 'List of course id. If empty return all courses
272 except front page course.',
274 ), 'options - operator OR is used', VALUE_DEFAULT
, array())
282 * @param array $options It contains an array (list of ids)
286 public static function get_courses($options = array()) {
288 require_once($CFG->dirroot
. "/course/lib.php");
291 $params = self
::validate_parameters(self
::get_courses_parameters(),
292 array('options' => $options));
295 if (!array_key_exists('ids', $params['options'])
296 or empty($params['options']['ids'])) {
297 $courses = $DB->get_records('course');
299 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
302 //create return value
303 $coursesinfo = array();
304 foreach ($courses as $course) {
306 // now security checks
307 $context = context_course
::instance($course->id
, IGNORE_MISSING
);
308 $courseformatoptions = course_get_format($course)->get_format_options();
310 self
::validate_context($context);
311 } catch (Exception
$e) {
312 $exceptionparam = new stdClass();
313 $exceptionparam->message
= $e->getMessage();
314 $exceptionparam->courseid
= $course->id
;
315 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
317 require_capability('moodle/course:view', $context);
319 $courseinfo = array();
320 $courseinfo['id'] = $course->id
;
321 $courseinfo['fullname'] = $course->fullname
;
322 $courseinfo['shortname'] = $course->shortname
;
323 $courseinfo['categoryid'] = $course->category
;
324 list($courseinfo['summary'], $courseinfo['summaryformat']) =
325 external_format_text($course->summary
, $course->summaryformat
, $context->id
, 'course', 'summary', 0);
326 $courseinfo['format'] = $course->format
;
327 $courseinfo['startdate'] = $course->startdate
;
328 if (array_key_exists('numsections', $courseformatoptions)) {
329 // For backward-compartibility
330 $courseinfo['numsections'] = $courseformatoptions['numsections'];
333 //some field should be returned only if the user has update permission
334 $courseadmin = has_capability('moodle/course:update', $context);
336 $courseinfo['categorysortorder'] = $course->sortorder
;
337 $courseinfo['idnumber'] = $course->idnumber
;
338 $courseinfo['showgrades'] = $course->showgrades
;
339 $courseinfo['showreports'] = $course->showreports
;
340 $courseinfo['newsitems'] = $course->newsitems
;
341 $courseinfo['visible'] = $course->visible
;
342 $courseinfo['maxbytes'] = $course->maxbytes
;
343 if (array_key_exists('hiddensections', $courseformatoptions)) {
344 // For backward-compartibility
345 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
347 $courseinfo['groupmode'] = $course->groupmode
;
348 $courseinfo['groupmodeforce'] = $course->groupmodeforce
;
349 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid
;
350 $courseinfo['lang'] = $course->lang
;
351 $courseinfo['timecreated'] = $course->timecreated
;
352 $courseinfo['timemodified'] = $course->timemodified
;
353 $courseinfo['forcetheme'] = $course->theme
;
354 $courseinfo['enablecompletion'] = $course->enablecompletion
;
355 $courseinfo['completionnotify'] = $course->completionnotify
;
356 $courseinfo['courseformatoptions'] = array();
357 foreach ($courseformatoptions as $key => $value) {
358 $courseinfo['courseformatoptions'][] = array(
365 if ($courseadmin or $course->visible
366 or has_capability('moodle/course:viewhiddencourses', $context)) {
367 $coursesinfo[] = $courseinfo;
375 * Returns description of method result value
377 * @return external_description
380 public static function get_courses_returns() {
381 return new external_multiple_structure(
382 new external_single_structure(
384 'id' => new external_value(PARAM_INT
, 'course id'),
385 'shortname' => new external_value(PARAM_TEXT
, 'course short name'),
386 'categoryid' => new external_value(PARAM_INT
, 'category id'),
387 'categorysortorder' => new external_value(PARAM_INT
,
388 'sort order into the category', VALUE_OPTIONAL
),
389 'fullname' => new external_value(PARAM_TEXT
, 'full name'),
390 'idnumber' => new external_value(PARAM_RAW
, 'id number', VALUE_OPTIONAL
),
391 'summary' => new external_value(PARAM_RAW
, 'summary'),
392 'summaryformat' => new external_format_value('summary'),
393 'format' => new external_value(PARAM_PLUGIN
,
394 'course format: weeks, topics, social, site,..'),
395 'showgrades' => new external_value(PARAM_INT
,
396 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL
),
397 'newsitems' => new external_value(PARAM_INT
,
398 'number of recent items appearing on the course page', VALUE_OPTIONAL
),
399 'startdate' => new external_value(PARAM_INT
,
400 'timestamp when the course start'),
401 'numsections' => new external_value(PARAM_INT
,
402 '(deprecated, use courseformatoptions) number of weeks/topics',
404 'maxbytes' => new external_value(PARAM_INT
,
405 'largest size of file that can be uploaded into the course',
407 'showreports' => new external_value(PARAM_INT
,
408 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL
),
409 'visible' => new external_value(PARAM_INT
,
410 '1: available to student, 0:not available', VALUE_OPTIONAL
),
411 'hiddensections' => new external_value(PARAM_INT
,
412 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
414 'groupmode' => new external_value(PARAM_INT
, 'no group, separate, visible',
416 'groupmodeforce' => new external_value(PARAM_INT
, '1: yes, 0: no',
418 'defaultgroupingid' => new external_value(PARAM_INT
, 'default grouping id',
420 'timecreated' => new external_value(PARAM_INT
,
421 'timestamp when the course have been created', VALUE_OPTIONAL
),
422 'timemodified' => new external_value(PARAM_INT
,
423 'timestamp when the course have been modified', VALUE_OPTIONAL
),
424 'enablecompletion' => new external_value(PARAM_INT
,
425 'Enabled, control via completion and activity settings. Disbaled,
426 not shown in activity settings.',
428 'completionnotify' => new external_value(PARAM_INT
,
429 '1: yes 0: no', VALUE_OPTIONAL
),
430 'lang' => new external_value(PARAM_SAFEDIR
,
431 'forced course language', VALUE_OPTIONAL
),
432 'forcetheme' => new external_value(PARAM_PLUGIN
,
433 'name of the force theme', VALUE_OPTIONAL
),
434 'courseformatoptions' => new external_multiple_structure(
435 new external_single_structure(
436 array('name' => new external_value(PARAM_ALPHANUMEXT
, 'course format option name'),
437 'value' => new external_value(PARAM_RAW
, 'course format option value')
439 'additional options for particular course format', VALUE_OPTIONAL
447 * Returns description of method parameters
449 * @return external_function_parameters
452 public static function create_courses_parameters() {
453 $courseconfig = get_config('moodlecourse'); //needed for many default values
454 return new external_function_parameters(
456 'courses' => new external_multiple_structure(
457 new external_single_structure(
459 'fullname' => new external_value(PARAM_TEXT
, 'full name'),
460 'shortname' => new external_value(PARAM_TEXT
, 'course short name'),
461 'categoryid' => new external_value(PARAM_INT
, 'category id'),
462 'idnumber' => new external_value(PARAM_RAW
, 'id number', VALUE_OPTIONAL
),
463 'summary' => new external_value(PARAM_RAW
, 'summary', VALUE_OPTIONAL
),
464 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT
),
465 'format' => new external_value(PARAM_PLUGIN
,
466 'course format: weeks, topics, social, site,..',
467 VALUE_DEFAULT
, $courseconfig->format
),
468 'showgrades' => new external_value(PARAM_INT
,
469 '1 if grades are shown, otherwise 0', VALUE_DEFAULT
,
470 $courseconfig->showgrades
),
471 'newsitems' => new external_value(PARAM_INT
,
472 'number of recent items appearing on the course page',
473 VALUE_DEFAULT
, $courseconfig->newsitems
),
474 'startdate' => new external_value(PARAM_INT
,
475 'timestamp when the course start', VALUE_OPTIONAL
),
476 'numsections' => new external_value(PARAM_INT
,
477 '(deprecated, use courseformatoptions) number of weeks/topics',
479 'maxbytes' => new external_value(PARAM_INT
,
480 'largest size of file that can be uploaded into the course',
481 VALUE_DEFAULT
, $courseconfig->maxbytes
),
482 'showreports' => new external_value(PARAM_INT
,
483 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT
,
484 $courseconfig->showreports
),
485 'visible' => new external_value(PARAM_INT
,
486 '1: available to student, 0:not available', VALUE_OPTIONAL
),
487 'hiddensections' => new external_value(PARAM_INT
,
488 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
490 'groupmode' => new external_value(PARAM_INT
, 'no group, separate, visible',
491 VALUE_DEFAULT
, $courseconfig->groupmode
),
492 'groupmodeforce' => new external_value(PARAM_INT
, '1: yes, 0: no',
493 VALUE_DEFAULT
, $courseconfig->groupmodeforce
),
494 'defaultgroupingid' => new external_value(PARAM_INT
, 'default grouping id',
496 'enablecompletion' => new external_value(PARAM_INT
,
497 'Enabled, control via completion and activity settings. Disabled,
498 not shown in activity settings.',
500 'completionnotify' => new external_value(PARAM_INT
,
501 '1: yes 0: no', VALUE_OPTIONAL
),
502 'lang' => new external_value(PARAM_SAFEDIR
,
503 'forced course language', VALUE_OPTIONAL
),
504 'forcetheme' => new external_value(PARAM_PLUGIN
,
505 'name of the force theme', VALUE_OPTIONAL
),
506 'courseformatoptions' => new external_multiple_structure(
507 new external_single_structure(
508 array('name' => new external_value(PARAM_ALPHANUMEXT
, 'course format option name'),
509 'value' => new external_value(PARAM_RAW
, 'course format option value')
511 'additional options for particular course format', VALUE_OPTIONAL
),
513 ), 'courses to create'
522 * @param array $courses
523 * @return array courses (id and shortname only)
526 public static function create_courses($courses) {
528 require_once($CFG->dirroot
. "/course/lib.php");
529 require_once($CFG->libdir
. '/completionlib.php');
531 $params = self
::validate_parameters(self
::create_courses_parameters(),
532 array('courses' => $courses));
534 $availablethemes = core_component
::get_plugin_list('theme');
535 $availablelangs = get_string_manager()->get_list_of_translations();
537 $transaction = $DB->start_delegated_transaction();
539 foreach ($params['courses'] as $course) {
541 // Ensure the current user is allowed to run this function
542 $context = context_coursecat
::instance($course['categoryid'], IGNORE_MISSING
);
544 self
::validate_context($context);
545 } catch (Exception
$e) {
546 $exceptionparam = new stdClass();
547 $exceptionparam->message
= $e->getMessage();
548 $exceptionparam->catid
= $course['categoryid'];
549 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
551 require_capability('moodle/course:create', $context);
553 // Make sure lang is valid
554 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
555 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
558 // Make sure theme is valid
559 if (array_key_exists('forcetheme', $course)) {
560 if (!empty($CFG->allowcoursethemes
)) {
561 if (empty($availablethemes[$course['forcetheme']])) {
562 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
564 $course['theme'] = $course['forcetheme'];
569 //force visibility if ws user doesn't have the permission to set it
570 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
571 if (!has_capability('moodle/course:visibility', $context)) {
572 $course['visible'] = $category->visible
;
575 //set default value for completion
576 $courseconfig = get_config('moodlecourse');
577 if (completion_info
::is_enabled_for_site()) {
578 if (!array_key_exists('enablecompletion', $course)) {
579 $course['enablecompletion'] = $courseconfig->enablecompletion
;
582 $course['enablecompletion'] = 0;
585 $course['category'] = $course['categoryid'];
588 $course['summaryformat'] = external_validate_format($course['summaryformat']);
590 if (!empty($course['courseformatoptions'])) {
591 foreach ($course['courseformatoptions'] as $option) {
592 $course[$option['name']] = $option['value'];
596 //Note: create_course() core function check shortname, idnumber, category
597 $course['id'] = create_course((object) $course)->id
;
599 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
602 $transaction->allow_commit();
604 return $resultcourses;
608 * Returns description of method result value
610 * @return external_description
613 public static function create_courses_returns() {
614 return new external_multiple_structure(
615 new external_single_structure(
617 'id' => new external_value(PARAM_INT
, 'course id'),
618 'shortname' => new external_value(PARAM_TEXT
, 'short name'),
627 * @return external_function_parameters
630 public static function update_courses_parameters() {
631 return new external_function_parameters(
633 'courses' => new external_multiple_structure(
634 new external_single_structure(
636 'id' => new external_value(PARAM_INT
, 'ID of the course'),
637 'fullname' => new external_value(PARAM_TEXT
, 'full name', VALUE_OPTIONAL
),
638 'shortname' => new external_value(PARAM_TEXT
, 'course short name', VALUE_OPTIONAL
),
639 'categoryid' => new external_value(PARAM_INT
, 'category id', VALUE_OPTIONAL
),
640 'idnumber' => new external_value(PARAM_RAW
, 'id number', VALUE_OPTIONAL
),
641 'summary' => new external_value(PARAM_RAW
, 'summary', VALUE_OPTIONAL
),
642 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL
),
643 'format' => new external_value(PARAM_PLUGIN
,
644 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL
),
645 'showgrades' => new external_value(PARAM_INT
,
646 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL
),
647 'newsitems' => new external_value(PARAM_INT
,
648 'number of recent items appearing on the course page', VALUE_OPTIONAL
),
649 'startdate' => new external_value(PARAM_INT
,
650 'timestamp when the course start', VALUE_OPTIONAL
),
651 'numsections' => new external_value(PARAM_INT
,
652 '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL
),
653 'maxbytes' => new external_value(PARAM_INT
,
654 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL
),
655 'showreports' => new external_value(PARAM_INT
,
656 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL
),
657 'visible' => new external_value(PARAM_INT
,
658 '1: available to student, 0:not available', VALUE_OPTIONAL
),
659 'hiddensections' => new external_value(PARAM_INT
,
660 '(deprecated, use courseformatoptions) How the hidden sections in the course are
661 displayed to students', VALUE_OPTIONAL
),
662 'groupmode' => new external_value(PARAM_INT
, 'no group, separate, visible', VALUE_OPTIONAL
),
663 'groupmodeforce' => new external_value(PARAM_INT
, '1: yes, 0: no', VALUE_OPTIONAL
),
664 'defaultgroupingid' => new external_value(PARAM_INT
, 'default grouping id', VALUE_OPTIONAL
),
665 'enablecompletion' => new external_value(PARAM_INT
,
666 'Enabled, control via completion and activity settings. Disabled,
667 not shown in activity settings.', VALUE_OPTIONAL
),
668 'completionnotify' => new external_value(PARAM_INT
, '1: yes 0: no', VALUE_OPTIONAL
),
669 'lang' => new external_value(PARAM_SAFEDIR
, 'forced course language', VALUE_OPTIONAL
),
670 'forcetheme' => new external_value(PARAM_PLUGIN
, 'name of the force theme', VALUE_OPTIONAL
),
671 'courseformatoptions' => new external_multiple_structure(
672 new external_single_structure(
673 array('name' => new external_value(PARAM_ALPHANUMEXT
, 'course format option name'),
674 'value' => new external_value(PARAM_RAW
, 'course format option value')
676 'additional options for particular course format', VALUE_OPTIONAL
),
678 ), 'courses to update'
687 * @param array $courses
690 public static function update_courses($courses) {
692 require_once($CFG->dirroot
. "/course/lib.php");
695 $params = self
::validate_parameters(self
::update_courses_parameters(),
696 array('courses' => $courses));
698 $availablethemes = core_component
::get_plugin_list('theme');
699 $availablelangs = get_string_manager()->get_list_of_translations();
701 foreach ($params['courses'] as $course) {
702 // Catch any exception while updating course and return as warning to user.
704 // Ensure the current user is allowed to run this function.
705 $context = context_course
::instance($course['id'], MUST_EXIST
);
706 self
::validate_context($context);
708 $oldcourse = course_get_format($course['id'])->get_course();
710 require_capability('moodle/course:update', $context);
712 // Check if user can change category.
713 if (array_key_exists('categoryid', $course) && ($oldcourse->category
!= $course['categoryid'])) {
714 require_capability('moodle/course:changecategory', $context);
715 $course['category'] = $course['categoryid'];
718 // Check if the user can change fullname.
719 if (array_key_exists('fullname', $course) && ($oldcourse->fullname
!= $course['fullname'])) {
720 require_capability('moodle/course:changefullname', $context);
723 // Check if the user can change shortname.
724 if (array_key_exists('shortname', $course) && ($oldcourse->shortname
!= $course['shortname'])) {
725 require_capability('moodle/course:changeshortname', $context);
728 // Check if the user can change the idnumber.
729 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber
!= $course['idnumber'])) {
730 require_capability('moodle/course:changeidnumber', $context);
733 // Check if user can change summary.
734 if (array_key_exists('summary', $course) && ($oldcourse->summary
!= $course['summary'])) {
735 require_capability('moodle/course:changesummary', $context);
739 if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat
!= $course['summaryformat'])) {
740 require_capability('moodle/course:changesummary', $context);
741 $course['summaryformat'] = external_validate_format($course['summaryformat']);
744 // Check if user can change visibility.
745 if (array_key_exists('visible', $course) && ($oldcourse->visible
!= $course['visible'])) {
746 require_capability('moodle/course:visibility', $context);
749 // Make sure lang is valid.
750 if (array_key_exists('lang', $course) && empty($availablelangs[$course['lang']])) {
751 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
754 // Make sure theme is valid.
755 if (array_key_exists('forcetheme', $course)) {
756 if (!empty($CFG->allowcoursethemes
)) {
757 if (empty($availablethemes[$course['forcetheme']])) {
758 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
760 $course['theme'] = $course['forcetheme'];
765 // Make sure completion is enabled before setting it.
766 if (array_key_exists('enabledcompletion', $course) && !completion_info
::is_enabled_for_site()) {
767 $course['enabledcompletion'] = 0;
770 // Make sure maxbytes are less then CFG->maxbytes.
771 if (array_key_exists('maxbytes', $course)) {
772 $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes
, $course['maxbytes']);
775 if (!empty($course['courseformatoptions'])) {
776 foreach ($course['courseformatoptions'] as $option) {
777 if (isset($option['name']) && isset($option['value'])) {
778 $course[$option['name']] = $option['value'];
783 // Update course if user has all required capabilities.
784 update_course((object) $course);
785 } catch (Exception
$e) {
787 $warning['item'] = 'course';
788 $warning['itemid'] = $course['id'];
789 if ($e instanceof moodle_exception
) {
790 $warning['warningcode'] = $e->errorcode
;
792 $warning['warningcode'] = $e->getCode();
794 $warning['message'] = $e->getMessage();
795 $warnings[] = $warning;
800 $result['warnings'] = $warnings;
805 * Returns description of method result value
807 * @return external_description
810 public static function update_courses_returns() {
811 return new external_single_structure(
813 'warnings' => new external_warnings()
819 * Returns description of method parameters
821 * @return external_function_parameters
824 public static function delete_courses_parameters() {
825 return new external_function_parameters(
827 'courseids' => new external_multiple_structure(new external_value(PARAM_INT
, 'course ID')),
835 * @param array $courseids A list of course ids
838 public static function delete_courses($courseids) {
840 require_once($CFG->dirroot
."/course/lib.php");
842 // Parameter validation.
843 $params = self
::validate_parameters(self
::delete_courses_parameters(), array('courseids'=>$courseids));
845 $transaction = $DB->start_delegated_transaction();
847 foreach ($params['courseids'] as $courseid) {
848 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST
);
850 // Check if the context is valid.
851 $coursecontext = context_course
::instance($course->id
);
852 self
::validate_context($coursecontext);
854 // Check if the current user has enought permissions.
855 if (!can_delete_course($courseid)) {
856 fix_course_sortorder();
857 throw new moodle_exception('cannotdeletecategorycourse', 'error',
858 '', format_string($course->fullname
)." (id: $courseid)");
861 delete_course($course, false);
864 $transaction->allow_commit();
865 fix_course_sortorder();
871 * Returns description of method result value
873 * @return external_description
876 public static function delete_courses_returns() {
881 * Returns description of method parameters
883 * @return external_function_parameters
886 public static function duplicate_course_parameters() {
887 return new external_function_parameters(
889 'courseid' => new external_value(PARAM_INT
, 'course to duplicate id'),
890 'fullname' => new external_value(PARAM_TEXT
, 'duplicated course full name'),
891 'shortname' => new external_value(PARAM_TEXT
, 'duplicated course short name'),
892 'categoryid' => new external_value(PARAM_INT
, 'duplicated course category parent'),
893 'visible' => new external_value(PARAM_INT
, 'duplicated course visible, default to yes', VALUE_DEFAULT
, 1),
894 'options' => new external_multiple_structure(
895 new external_single_structure(
897 'name' => new external_value(PARAM_ALPHAEXT
, 'The backup option name:
898 "activities" (int) Include course activites (default to 1 that is equal to yes),
899 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
900 "filters" (int) Include course filters (default to 1 that is equal to yes),
901 "users" (int) Include users (default to 0 that is equal to no),
902 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
903 "comments" (int) Include user comments (default to 0 that is equal to no),
904 "userscompletion" (int) Include user course completion information (default to 0 that is equal to no),
905 "logs" (int) Include course logs (default to 0 that is equal to no),
906 "grade_histories" (int) Include histories (default to 0 that is equal to no)'
908 'value' => new external_value(PARAM_RAW
, 'the value for the option 1 (yes) or 0 (no)'
911 ), VALUE_DEFAULT
, array()
920 * @param int $courseid
921 * @param string $fullname Duplicated course fullname
922 * @param string $shortname Duplicated course shortname
923 * @param int $categoryid Duplicated course parent category id
924 * @param int $visible Duplicated course availability
925 * @param array $options List of backup options
926 * @return array New course info
929 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
930 global $CFG, $USER, $DB;
931 require_once($CFG->dirroot
. '/backup/util/includes/backup_includes.php');
932 require_once($CFG->dirroot
. '/backup/util/includes/restore_includes.php');
934 // Parameter validation.
935 $params = self
::validate_parameters(
936 self
::duplicate_course_parameters(),
938 'courseid' => $courseid,
939 'fullname' => $fullname,
940 'shortname' => $shortname,
941 'categoryid' => $categoryid,
942 'visible' => $visible,
943 'options' => $options
947 // Context validation.
949 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
950 throw new moodle_exception('invalidcourseid', 'error');
953 // Category where duplicated course is going to be created.
954 $categorycontext = context_coursecat
::instance($params['categoryid']);
955 self
::validate_context($categorycontext);
957 // Course to be duplicated.
958 $coursecontext = context_course
::instance($course->id
);
959 self
::validate_context($coursecontext);
961 $backupdefaults = array(
966 'role_assignments' => 0,
968 'userscompletion' => 0,
970 'grade_histories' => 0
973 $backupsettings = array();
974 // Check for backup and restore options.
975 if (!empty($params['options'])) {
976 foreach ($params['options'] as $option) {
978 // Strict check for a correct value (allways 1 or 0, true or false).
979 $value = clean_param($option['value'], PARAM_INT
);
981 if ($value !== 0 and $value !== 1) {
982 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
985 if (!isset($backupdefaults[$option['name']])) {
986 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
989 $backupsettings[$option['name']] = $value;
993 // Capability checking.
995 // The backup controller check for this currently, this may be redundant.
996 require_capability('moodle/course:create', $categorycontext);
997 require_capability('moodle/restore:restorecourse', $categorycontext);
998 require_capability('moodle/backup:backupcourse', $coursecontext);
1000 if (!empty($backupsettings['users'])) {
1001 require_capability('moodle/backup:userinfo', $coursecontext);
1002 require_capability('moodle/restore:userinfo', $categorycontext);
1005 // Check if the shortname is used.
1006 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
1007 foreach ($foundcourses as $foundcourse) {
1008 $foundcoursenames[] = $foundcourse->fullname
;
1011 $foundcoursenamestring = implode(',', $foundcoursenames);
1012 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1015 // Backup the course.
1017 $bc = new backup_controller(backup
::TYPE_1COURSE
, $course->id
, backup
::FORMAT_MOODLE
,
1018 backup
::INTERACTIVE_NO
, backup
::MODE_SAMESITE
, $USER->id
);
1020 foreach ($backupsettings as $name => $value) {
1021 $bc->get_plan()->get_setting($name)->set_value($value);
1024 $backupid = $bc->get_backupid();
1025 $backupbasepath = $bc->get_plan()->get_basepath();
1027 $bc->execute_plan();
1028 $results = $bc->get_results();
1029 $file = $results['backup_destination'];
1033 // Restore the backup immediately.
1035 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
1036 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
1037 $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath);
1040 // Create new course.
1041 $newcourseid = restore_dbops
::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1043 $rc = new restore_controller($backupid, $newcourseid,
1044 backup
::INTERACTIVE_NO
, backup
::MODE_SAMESITE
, $USER->id
, backup
::TARGET_NEW_COURSE
);
1046 foreach ($backupsettings as $name => $value) {
1047 $setting = $rc->get_plan()->get_setting($name);
1048 if ($setting->get_status() == backup_setting
::NOT_LOCKED
) {
1049 $setting->set_value($value);
1053 if (!$rc->execute_precheck()) {
1054 $precheckresults = $rc->get_precheck_results();
1055 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1056 if (empty($CFG->keeptempdirectoriesonbackup
)) {
1057 fulldelete($backupbasepath);
1062 foreach ($precheckresults['errors'] as $error) {
1063 $errorinfo .= $error;
1066 if (array_key_exists('warnings', $precheckresults)) {
1067 foreach ($precheckresults['warnings'] as $warning) {
1068 $errorinfo .= $warning;
1072 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1076 $rc->execute_plan();
1079 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST
);
1080 $course->fullname
= $params['fullname'];
1081 $course->shortname
= $params['shortname'];
1082 $course->visible
= $params['visible'];
1084 // Set shortname and fullname back.
1085 $DB->update_record('course', $course);
1087 if (empty($CFG->keeptempdirectoriesonbackup
)) {
1088 fulldelete($backupbasepath);
1091 // Delete the course backup file created by this WebService. Originally located in the course backups area.
1094 return array('id' => $course->id
, 'shortname' => $course->shortname
);
1098 * Returns description of method result value
1100 * @return external_description
1103 public static function duplicate_course_returns() {
1104 return new external_single_structure(
1106 'id' => new external_value(PARAM_INT
, 'course id'),
1107 'shortname' => new external_value(PARAM_TEXT
, 'short name'),
1113 * Returns description of method parameters for import_course
1115 * @return external_function_parameters
1118 public static function import_course_parameters() {
1119 return new external_function_parameters(
1121 'importfrom' => new external_value(PARAM_INT
, 'the id of the course we are importing from'),
1122 'importto' => new external_value(PARAM_INT
, 'the id of the course we are importing to'),
1123 'deletecontent' => new external_value(PARAM_INT
, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT
, 0),
1124 'options' => new external_multiple_structure(
1125 new external_single_structure(
1127 'name' => new external_value(PARAM_ALPHA
, 'The backup option name:
1128 "activities" (int) Include course activites (default to 1 that is equal to yes),
1129 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1130 "filters" (int) Include course filters (default to 1 that is equal to yes)'
1132 'value' => new external_value(PARAM_RAW
, 'the value for the option 1 (yes) or 0 (no)'
1135 ), VALUE_DEFAULT
, array()
1144 * @param int $importfrom The id of the course we are importing from
1145 * @param int $importto The id of the course we are importing to
1146 * @param bool $deletecontent Whether to delete the course we are importing to content
1147 * @param array $options List of backup options
1151 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
1152 global $CFG, $USER, $DB;
1153 require_once($CFG->dirroot
. '/backup/util/includes/backup_includes.php');
1154 require_once($CFG->dirroot
. '/backup/util/includes/restore_includes.php');
1156 // Parameter validation.
1157 $params = self
::validate_parameters(
1158 self
::import_course_parameters(),
1160 'importfrom' => $importfrom,
1161 'importto' => $importto,
1162 'deletecontent' => $deletecontent,
1163 'options' => $options
1167 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1168 throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
1171 // Context validation.
1173 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
1174 throw new moodle_exception('invalidcourseid', 'error');
1177 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
1178 throw new moodle_exception('invalidcourseid', 'error');
1181 $importfromcontext = context_course
::instance($importfrom->id
);
1182 self
::validate_context($importfromcontext);
1184 $importtocontext = context_course
::instance($importto->id
);
1185 self
::validate_context($importtocontext);
1187 $backupdefaults = array(
1193 $backupsettings = array();
1195 // Check for backup and restore options.
1196 if (!empty($params['options'])) {
1197 foreach ($params['options'] as $option) {
1199 // Strict check for a correct value (allways 1 or 0, true or false).
1200 $value = clean_param($option['value'], PARAM_INT
);
1202 if ($value !== 0 and $value !== 1) {
1203 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1206 if (!isset($backupdefaults[$option['name']])) {
1207 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1210 $backupsettings[$option['name']] = $value;
1214 // Capability checking.
1216 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1217 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1219 $bc = new backup_controller(backup
::TYPE_1COURSE
, $importfrom->id
, backup
::FORMAT_MOODLE
,
1220 backup
::INTERACTIVE_NO
, backup
::MODE_IMPORT
, $USER->id
);
1222 foreach ($backupsettings as $name => $value) {
1223 $bc->get_plan()->get_setting($name)->set_value($value);
1226 $backupid = $bc->get_backupid();
1227 $backupbasepath = $bc->get_plan()->get_basepath();
1229 $bc->execute_plan();
1232 // Restore the backup immediately.
1234 // Check if we must delete the contents of the destination course.
1235 if ($params['deletecontent']) {
1236 $restoretarget = backup
::TARGET_EXISTING_DELETING
;
1238 $restoretarget = backup
::TARGET_EXISTING_ADDING
;
1241 $rc = new restore_controller($backupid, $importto->id
,
1242 backup
::INTERACTIVE_NO
, backup
::MODE_IMPORT
, $USER->id
, $restoretarget);
1244 foreach ($backupsettings as $name => $value) {
1245 $rc->get_plan()->get_setting($name)->set_value($value);
1248 if (!$rc->execute_precheck()) {
1249 $precheckresults = $rc->get_precheck_results();
1250 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1251 if (empty($CFG->keeptempdirectoriesonbackup
)) {
1252 fulldelete($backupbasepath);
1257 foreach ($precheckresults['errors'] as $error) {
1258 $errorinfo .= $error;
1261 if (array_key_exists('warnings', $precheckresults)) {
1262 foreach ($precheckresults['warnings'] as $warning) {
1263 $errorinfo .= $warning;
1267 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1270 if ($restoretarget == backup
::TARGET_EXISTING_DELETING
) {
1271 restore_dbops
::delete_course_content($importto->id
);
1275 $rc->execute_plan();
1278 if (empty($CFG->keeptempdirectoriesonbackup
)) {
1279 fulldelete($backupbasepath);
1286 * Returns description of method result value
1288 * @return external_description
1291 public static function import_course_returns() {
1296 * Returns description of method parameters
1298 * @return external_function_parameters
1301 public static function get_categories_parameters() {
1302 return new external_function_parameters(
1304 'criteria' => new external_multiple_structure(
1305 new external_single_structure(
1307 'key' => new external_value(PARAM_ALPHA
,
1308 'The category column to search, expected keys (value format) are:'.
1309 '"id" (int) the category id,'.
1310 '"name" (string) the category name,'.
1311 '"parent" (int) the parent category id,'.
1312 '"idnumber" (string) category idnumber'.
1313 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
1314 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1315 then the function return all categories that the user can see.'.
1316 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1317 '"theme" (string) only return the categories having this theme'.
1318 ' - user must have \'moodle/category:manage\' to search on theme'),
1319 'value' => new external_value(PARAM_RAW
, 'the value to match')
1321 ), 'criteria', VALUE_DEFAULT
, array()
1323 'addsubcategories' => new external_value(PARAM_BOOL
, 'return the sub categories infos
1324 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT
, 1)
1332 * @param array $criteria Criteria to match the results
1333 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1334 * @return array list of categories
1337 public static function get_categories($criteria = array(), $addsubcategories = true) {
1339 require_once($CFG->dirroot
. "/course/lib.php");
1341 // Validate parameters.
1342 $params = self
::validate_parameters(self
::get_categories_parameters(),
1343 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1345 // Retrieve the categories.
1346 $categories = array();
1347 if (!empty($params['criteria'])) {
1349 $conditions = array();
1351 foreach ($params['criteria'] as $crit) {
1352 $key = trim($crit['key']);
1354 // Trying to avoid duplicate keys.
1355 if (!isset($conditions[$key])) {
1357 $context = context_system
::instance();
1361 $value = clean_param($crit['value'], PARAM_INT
);
1365 if (has_capability('moodle/category:manage', $context)) {
1366 $value = clean_param($crit['value'], PARAM_RAW
);
1368 // We must throw an exception.
1369 // Otherwise the dev client would think no idnumber exists.
1370 throw new moodle_exception('criteriaerror',
1371 'webservice', '', null,
1372 'You don\'t have the permissions to search on the "idnumber" field.');
1377 $value = clean_param($crit['value'], PARAM_TEXT
);
1381 $value = clean_param($crit['value'], PARAM_INT
);
1385 if (has_capability('moodle/category:manage', $context)
1386 or has_capability('moodle/category:viewhiddencategories',
1387 context_system
::instance())) {
1388 $value = clean_param($crit['value'], PARAM_INT
);
1390 throw new moodle_exception('criteriaerror',
1391 'webservice', '', null,
1392 'You don\'t have the permissions to search on the "visible" field.');
1397 if (has_capability('moodle/category:manage', $context)) {
1398 $value = clean_param($crit['value'], PARAM_THEME
);
1400 throw new moodle_exception('criteriaerror',
1401 'webservice', '', null,
1402 'You don\'t have the permissions to search on the "theme" field.');
1407 throw new moodle_exception('criteriaerror',
1408 'webservice', '', null,
1409 'You can not search on this criteria: ' . $key);
1412 if (isset($value)) {
1413 $conditions[$key] = $crit['value'];
1414 $wheres[] = $key . " = :" . $key;
1419 if (!empty($wheres)) {
1420 $wheres = implode(" AND ", $wheres);
1422 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1424 // Retrieve its sub subcategories (all levels).
1425 if ($categories and !empty($params['addsubcategories'])) {
1426 $newcategories = array();
1428 // Check if we required visible/theme checks.
1429 $additionalselect = '';
1430 $additionalparams = array();
1431 if (isset($conditions['visible'])) {
1432 $additionalselect .= ' AND visible = :visible';
1433 $additionalparams['visible'] = $conditions['visible'];
1435 if (isset($conditions['theme'])) {
1436 $additionalselect .= ' AND theme= :theme';
1437 $additionalparams['theme'] = $conditions['theme'];
1440 foreach ($categories as $category) {
1441 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1442 $sqlparams = array('path' => $category->path
.'/%') +
$additionalparams; // It will NOT include the specified category.
1443 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
1444 $newcategories = $newcategories +
$subcategories; // Both arrays have integer as keys.
1446 $categories = $categories +
$newcategories;
1451 // Retrieve all categories in the database.
1452 $categories = $DB->get_records('course_categories');
1455 // The not returned categories. key => category id, value => reason of exclusion.
1456 $excludedcats = array();
1458 // The returned categories.
1459 $categoriesinfo = array();
1461 // We need to sort the categories by path.
1462 // The parent cats need to be checked by the algo first.
1463 usort($categories, "core_course_external::compare_categories_by_path");
1465 foreach ($categories as $category) {
1467 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1468 $parents = explode('/', $category->path
);
1469 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1470 foreach ($parents as $parentid) {
1471 // Note: when the parent exclusion was due to the context,
1472 // the sub category could still be returned.
1473 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1474 $excludedcats[$category->id
] = 'parent';
1478 // Check category depth is <= maxdepth (do not check for user who can manage categories).
1479 if ((!empty($CFG->maxcategorydepth
) && count($parents) > $CFG->maxcategorydepth
)
1480 and !has_capability('moodle/category:manage', $context)) {
1481 $excludedcats[$category->id
] = 'depth';
1484 // Check the user can use the category context.
1485 $context = context_coursecat
::instance($category->id
);
1487 self
::validate_context($context);
1488 } catch (Exception
$e) {
1489 $excludedcats[$category->id
] = 'context';
1491 // If it was the requested category then throw an exception.
1492 if (isset($params['categoryid']) && $category->id
== $params['categoryid']) {
1493 $exceptionparam = new stdClass();
1494 $exceptionparam->message
= $e->getMessage();
1495 $exceptionparam->catid
= $category->id
;
1496 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1500 // Return the category information.
1501 if (!isset($excludedcats[$category->id
])) {
1503 // Final check to see if the category is visible to the user.
1504 if ($category->visible
1505 or has_capability('moodle/category:viewhiddencategories', context_system
::instance())
1506 or has_capability('moodle/category:manage', $context)) {
1508 $categoryinfo = array();
1509 $categoryinfo['id'] = $category->id
;
1510 $categoryinfo['name'] = $category->name
;
1511 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1512 external_format_text($category->description
, $category->descriptionformat
,
1513 $context->id
, 'coursecat', 'description', null);
1514 $categoryinfo['parent'] = $category->parent
;
1515 $categoryinfo['sortorder'] = $category->sortorder
;
1516 $categoryinfo['coursecount'] = $category->coursecount
;
1517 $categoryinfo['depth'] = $category->depth
;
1518 $categoryinfo['path'] = $category->path
;
1520 // Some fields only returned for admin.
1521 if (has_capability('moodle/category:manage', $context)) {
1522 $categoryinfo['idnumber'] = $category->idnumber
;
1523 $categoryinfo['visible'] = $category->visible
;
1524 $categoryinfo['visibleold'] = $category->visibleold
;
1525 $categoryinfo['timemodified'] = $category->timemodified
;
1526 $categoryinfo['theme'] = $category->theme
;
1529 $categoriesinfo[] = $categoryinfo;
1531 $excludedcats[$category->id
] = 'visibility';
1536 // Sorting the resulting array so it looks a bit better for the client developer.
1537 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
1539 return $categoriesinfo;
1543 * Sort categories array by path
1544 * private function: only used by get_categories
1546 * @param array $category1
1547 * @param array $category2
1548 * @return int result of strcmp
1551 private static function compare_categories_by_path($category1, $category2) {
1552 return strcmp($category1->path
, $category2->path
);
1556 * Sort categories array by sortorder
1557 * private function: only used by get_categories
1559 * @param array $category1
1560 * @param array $category2
1561 * @return int result of strcmp
1564 private static function compare_categories_by_sortorder($category1, $category2) {
1565 return strcmp($category1['sortorder'], $category2['sortorder']);
1569 * Returns description of method result value
1571 * @return external_description
1574 public static function get_categories_returns() {
1575 return new external_multiple_structure(
1576 new external_single_structure(
1578 'id' => new external_value(PARAM_INT
, 'category id'),
1579 'name' => new external_value(PARAM_TEXT
, 'category name'),
1580 'idnumber' => new external_value(PARAM_RAW
, 'category id number', VALUE_OPTIONAL
),
1581 'description' => new external_value(PARAM_RAW
, 'category description'),
1582 'descriptionformat' => new external_format_value('description'),
1583 'parent' => new external_value(PARAM_INT
, 'parent category id'),
1584 'sortorder' => new external_value(PARAM_INT
, 'category sorting order'),
1585 'coursecount' => new external_value(PARAM_INT
, 'number of courses in this category'),
1586 'visible' => new external_value(PARAM_INT
, '1: available, 0:not available', VALUE_OPTIONAL
),
1587 'visibleold' => new external_value(PARAM_INT
, '1: available, 0:not available', VALUE_OPTIONAL
),
1588 'timemodified' => new external_value(PARAM_INT
, 'timestamp', VALUE_OPTIONAL
),
1589 'depth' => new external_value(PARAM_INT
, 'category depth'),
1590 'path' => new external_value(PARAM_TEXT
, 'category path'),
1591 'theme' => new external_value(PARAM_THEME
, 'category theme', VALUE_OPTIONAL
),
1592 ), 'List of categories'
1598 * Returns description of method parameters
1600 * @return external_function_parameters
1603 public static function create_categories_parameters() {
1604 return new external_function_parameters(
1606 'categories' => new external_multiple_structure(
1607 new external_single_structure(
1609 'name' => new external_value(PARAM_TEXT
, 'new category name'),
1610 'parent' => new external_value(PARAM_INT
,
1611 'the parent category id inside which the new category will be created
1612 - set to 0 for a root category',
1614 'idnumber' => new external_value(PARAM_RAW
,
1615 'the new category idnumber', VALUE_OPTIONAL
),
1616 'description' => new external_value(PARAM_RAW
,
1617 'the new category description', VALUE_OPTIONAL
),
1618 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT
),
1619 'theme' => new external_value(PARAM_THEME
,
1620 'the new category theme. This option must be enabled on moodle',
1632 * @param array $categories - see create_categories_parameters() for the array structure
1633 * @return array - see create_categories_returns() for the array structure
1636 public static function create_categories($categories) {
1638 require_once($CFG->libdir
. "/coursecatlib.php");
1640 $params = self
::validate_parameters(self
::create_categories_parameters(),
1641 array('categories' => $categories));
1643 $transaction = $DB->start_delegated_transaction();
1645 $createdcategories = array();
1646 foreach ($params['categories'] as $category) {
1647 if ($category['parent']) {
1648 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1649 throw new moodle_exception('unknowcategory');
1651 $context = context_coursecat
::instance($category['parent']);
1653 $context = context_system
::instance();
1655 self
::validate_context($context);
1656 require_capability('moodle/category:manage', $context);
1658 // this will validate format and throw an exception if there are errors
1659 external_validate_format($category['descriptionformat']);
1661 $newcategory = coursecat
::create($category);
1663 $createdcategories[] = array('id' => $newcategory->id
, 'name' => $newcategory->name
);
1666 $transaction->allow_commit();
1668 return $createdcategories;
1672 * Returns description of method parameters
1674 * @return external_function_parameters
1677 public static function create_categories_returns() {
1678 return new external_multiple_structure(
1679 new external_single_structure(
1681 'id' => new external_value(PARAM_INT
, 'new category id'),
1682 'name' => new external_value(PARAM_TEXT
, 'new category name'),
1689 * Returns description of method parameters
1691 * @return external_function_parameters
1694 public static function update_categories_parameters() {
1695 return new external_function_parameters(
1697 'categories' => new external_multiple_structure(
1698 new external_single_structure(
1700 'id' => new external_value(PARAM_INT
, 'course id'),
1701 'name' => new external_value(PARAM_TEXT
, 'category name', VALUE_OPTIONAL
),
1702 'idnumber' => new external_value(PARAM_RAW
, 'category id number', VALUE_OPTIONAL
),
1703 'parent' => new external_value(PARAM_INT
, 'parent category id', VALUE_OPTIONAL
),
1704 'description' => new external_value(PARAM_RAW
, 'category description', VALUE_OPTIONAL
),
1705 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT
),
1706 'theme' => new external_value(PARAM_THEME
,
1707 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL
),
1718 * @param array $categories The list of categories to update
1722 public static function update_categories($categories) {
1724 require_once($CFG->libdir
. "/coursecatlib.php");
1726 // Validate parameters.
1727 $params = self
::validate_parameters(self
::update_categories_parameters(), array('categories' => $categories));
1729 $transaction = $DB->start_delegated_transaction();
1731 foreach ($params['categories'] as $cat) {
1732 $category = coursecat
::get($cat['id']);
1734 $categorycontext = context_coursecat
::instance($cat['id']);
1735 self
::validate_context($categorycontext);
1736 require_capability('moodle/category:manage', $categorycontext);
1738 // this will throw an exception if descriptionformat is not valid
1739 external_validate_format($cat['descriptionformat']);
1741 $category->update($cat);
1744 $transaction->allow_commit();
1748 * Returns description of method result value
1750 * @return external_description
1753 public static function update_categories_returns() {
1758 * Returns description of method parameters
1760 * @return external_function_parameters
1763 public static function delete_categories_parameters() {
1764 return new external_function_parameters(
1766 'categories' => new external_multiple_structure(
1767 new external_single_structure(
1769 'id' => new external_value(PARAM_INT
, 'category id to delete'),
1770 'newparent' => new external_value(PARAM_INT
,
1771 'the parent category to move the contents to, if specified', VALUE_OPTIONAL
),
1772 'recursive' => new external_value(PARAM_BOOL
, '1: recursively delete all contents inside this
1773 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT
, 0)
1784 * @param array $categories A list of category ids
1788 public static function delete_categories($categories) {
1790 require_once($CFG->dirroot
. "/course/lib.php");
1791 require_once($CFG->libdir
. "/coursecatlib.php");
1793 // Validate parameters.
1794 $params = self
::validate_parameters(self
::delete_categories_parameters(), array('categories' => $categories));
1796 $transaction = $DB->start_delegated_transaction();
1798 foreach ($params['categories'] as $category) {
1799 $deletecat = coursecat
::get($category['id'], MUST_EXIST
);
1800 $context = context_coursecat
::instance($deletecat->id
);
1801 require_capability('moodle/category:manage', $context);
1802 self
::validate_context($context);
1803 self
::validate_context(get_category_or_system_context($deletecat->parent
));
1805 if ($category['recursive']) {
1806 // If recursive was specified, then we recursively delete the category's contents.
1807 if ($deletecat->can_delete_full()) {
1808 $deletecat->delete_full(false);
1810 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1813 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1814 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1815 // We must move to an existing category.
1816 if (!empty($category['newparent'])) {
1817 $newparentcat = coursecat
::get($category['newparent']);
1819 $newparentcat = coursecat
::get($deletecat->parent
);
1822 // This operation is not allowed. We must move contents to an existing category.
1823 if (!$newparentcat->id
) {
1824 throw new moodle_exception('movecatcontentstoroot');
1827 self
::validate_context(context_coursecat
::instance($newparentcat->id
));
1828 if ($deletecat->can_move_content_to($newparentcat->id
)) {
1829 $deletecat->delete_move($newparentcat->id
, false);
1831 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1836 $transaction->allow_commit();
1840 * Returns description of method parameters
1842 * @return external_function_parameters
1845 public static function delete_categories_returns() {
1850 * Describes the parameters for delete_modules.
1852 * @return external_external_function_parameters
1855 public static function delete_modules_parameters() {
1856 return new external_function_parameters (
1858 'cmids' => new external_multiple_structure(new external_value(PARAM_INT
, 'course module ID',
1859 VALUE_REQUIRED
, '', NULL_NOT_ALLOWED
), 'Array of course module IDs'),
1865 * Deletes a list of provided module instances.
1867 * @param array $cmids the course module ids
1870 public static function delete_modules($cmids) {
1873 // Require course file containing the course delete module function.
1874 require_once($CFG->dirroot
. "/course/lib.php");
1876 // Clean the parameters.
1877 $params = self
::validate_parameters(self
::delete_modules_parameters(), array('cmids' => $cmids));
1879 // Keep track of the course ids we have performed a capability check on to avoid repeating.
1880 $arrcourseschecked = array();
1882 foreach ($params['cmids'] as $cmid) {
1883 // Get the course module.
1884 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST
);
1886 // Check if we have not yet confirmed they have permission in this course.
1887 if (!in_array($cm->course
, $arrcourseschecked)) {
1888 // Ensure the current user has required permission in this course.
1889 $context = context_course
::instance($cm->course
);
1890 self
::validate_context($context);
1891 // Add to the array.
1892 $arrcourseschecked[] = $cm->course
;
1895 // Ensure they can delete this module.
1896 $modcontext = context_module
::instance($cm->id
);
1897 require_capability('moodle/course:manageactivities', $modcontext);
1899 // Delete the module.
1900 course_delete_module($cm->id
);
1905 * Describes the delete_modules return value.
1907 * @return external_single_structure
1910 public static function delete_modules_returns() {
1916 * Deprecated course external functions
1918 * @package core_course
1919 * @copyright 2009 Petr Skodak
1920 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1922 * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
1923 * @see core_course_external
1925 class moodle_course_external
extends external_api
{
1928 * Returns description of method parameters
1930 * @return external_function_parameters
1932 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1933 * @see core_course_external::get_courses_parameters()
1935 public static function get_courses_parameters() {
1936 return core_course_external
::get_courses_parameters();
1942 * @param array $options
1945 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1946 * @see core_course_external::get_courses()
1948 public static function get_courses($options) {
1949 return core_course_external
::get_courses($options);
1953 * Returns description of method result value
1955 * @return external_description
1957 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1958 * @see core_course_external::get_courses_returns()
1960 public static function get_courses_returns() {
1961 return core_course_external
::get_courses_returns();
1965 * Returns description of method parameters
1967 * @return external_function_parameters
1969 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1970 * @see core_course_external::create_courses_parameters()
1972 public static function create_courses_parameters() {
1973 return core_course_external
::create_courses_parameters();
1979 * @param array $courses
1980 * @return array courses (id and shortname only)
1982 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1983 * @see core_course_external::create_courses()
1985 public static function create_courses($courses) {
1986 return core_course_external
::create_courses($courses);
1990 * Returns description of method result value
1992 * @return external_description
1994 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1995 * @see core_course_external::create_courses_returns()
1997 public static function create_courses_returns() {
1998 return core_course_external
::create_courses_returns();