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