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