Merge branch 'w24_MDL-33204_m23_importsort' of git://github.com/skodak/moodle
[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_TEXT, 'activity module name'),
216                                     'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
217                                     'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
218                                     'modicon' => new external_value(PARAM_URL, 'activity icon url'),
219                                     'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
220                                     'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
221                                     'availablefrom' => new external_value(PARAM_INT, 'module availability start date', VALUE_OPTIONAL),
222                                     'availableuntil' => new external_value(PARAM_INT, 'module availability en date', VALUE_OPTIONAL),
223                                     'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
224                                     'contents' => new external_multiple_structure(
225                                           new external_single_structure(
226                                               array(
227                                                   // content info
228                                                   'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
229                                                   'filename'=> new external_value(PARAM_FILE, 'filename'),
230                                                   'filepath'=> new external_value(PARAM_PATH, 'filepath'),
231                                                   'filesize'=> new external_value(PARAM_INT, 'filesize'),
232                                                   'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
233                                                   'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
234                                                   'timecreated' => new external_value(PARAM_INT, 'Time created'),
235                                                   'timemodified' => new external_value(PARAM_INT, 'Time modified'),
236                                                   'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
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 (!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 (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 (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 (!key_exists('enablecompletion', $course)) {
551                     $course['enablecompletion'] = $courseconfig->enablecompletion;
552                 }
553                 if (!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_ALPHA, 'The backup option name:
672                                             "activities" (int) Include course activites (default to 1 that is equal to yes),
673                                             "blocks" (int) Include course blocks (default to 1 that is equal to yes),
674                                             "filters" (int) Include course filters  (default to 1 that is equal to yes),
675                                             "users" (int) Include users (default to 0 that is equal to no),
676                                             "role_assignments" (int) Include role assignments  (default to 0 that is equal to no),
677                                             "user_files" (int) Include user files  (default to 0 that is equal to no),
678                                             "comments" (int) Include user comments  (default to 0 that is equal to no),
679                                             "completion_information" (int) Include user course completion information  (default to 0 that is equal to no),
680                                             "logs" (int) Include course logs  (default to 0 that is equal to no),
681                                             "histories" (int) Include histories  (default to 0 that is equal to no)'
682                                             ),
683                                 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
684                             )
685                         )
686                     ), VALUE_DEFAULT, array()
687                 ),
688             )
689         );
690     }
692     /**
693      * Duplicate a course
694      *
695      * @param int $courseid
696      * @param string $fullname Duplicated course fullname
697      * @param string $shortname Duplicated course shortname
698      * @param int $categoryid Duplicated course parent category id
699      * @param int $visible Duplicated course availability
700      * @param array $options List of backup options
701      * @return array New course info
702      * @since Moodle 2.3
703      */
704     public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
705         global $CFG, $USER, $DB;
706         require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
707         require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
709         // Parameter validation.
710         $params = self::validate_parameters(
711                 self::duplicate_course_parameters(),
712                 array(
713                       'courseid' => $courseid,
714                       'fullname' => $fullname,
715                       'shortname' => $shortname,
716                       'categoryid' => $categoryid,
717                       'visible' => $visible,
718                       'options' => $options
719                 )
720         );
722         // Context validation.
724         if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
725             throw new moodle_exception('invalidcourseid', 'error', '', $params['courseid']);
726         }
728         // Category where duplicated course is going to be created.
729         $categorycontext = context_coursecat::instance($params['categoryid']);
730         self::validate_context($categorycontext);
732         // Course to be duplicated.
733         $coursecontext = context_course::instance($course->id);
734         self::validate_context($coursecontext);
736         $backupdefaults = array(
737             'activities' => 1,
738             'blocks' => 1,
739             'filters' => 1,
740             'users' => 0,
741             'role_assignments' => 0,
742             'user_files' => 0,
743             'comments' => 0,
744             'completion_information' => 0,
745             'logs' => 0,
746             'histories' => 0
747         );
749         $backupsettings = array();
750         // Check for backup and restore options.
751         if (!empty($params['options'])) {
752             foreach ($params['options'] as $option) {
754                 // Strict check for a correct value (allways 1 or 0, true or false).
755                 $value = clean_param($option['value'], PARAM_INT);
757                 if ($value !== 0 and $value !== 1) {
758                     throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
759                 }
761                 if (!isset($backupdefaults[$option['name']])) {
762                     throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
763                 }
765                 $backupsettings[$option['name']] = $value;
766             }
767         }
769         // Capability checking.
771         // The backup controller check for this currently, this may be redundant.
772         require_capability('moodle/course:create', $categorycontext);
773         require_capability('moodle/restore:restorecourse', $categorycontext);
774         require_capability('moodle/backup:backupcourse', $coursecontext);
776         if (!empty($backupsettings['users'])) {
777             require_capability('moodle/backup:userinfo', $coursecontext);
778             require_capability('moodle/restore:userinfo', $categorycontext);
779         }
781         // Check if the shortname is used.
782         if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
783             foreach ($foundcourses as $foundcourse) {
784                 $foundcoursenames[] = $foundcourse->fullname;
785             }
787             $foundcoursenamestring = implode(',', $foundcoursenames);
788             throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
789         }
791         // Backup the course.
793         $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
794         backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
796         foreach ($backupsettings as $name => $value) {
797             $bc->get_plan()->get_setting($name)->set_value($value);
798         }
800         $backupid       = $bc->get_backupid();
801         $backupbasepath = $bc->get_plan()->get_basepath();
803         $bc->execute_plan();
804         $results = $bc->get_results();
805         $file = $results['backup_destination'];
807         $bc->destroy();
809         // Restore the backup immediately.
811         // Check if we need to unzip the file because the backup temp dir does not contains backup files.
812         if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
813             $file->extract_to_pathname(get_file_packer(), $backupbasepath);
814         }
816         // Create new course.
817         $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
819         $rc = new restore_controller($backupid, $newcourseid,
820                 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
822         foreach ($backupsettings as $name => $value) {
823             $setting = $rc->get_plan()->get_setting($name);
824             if ($setting->get_status() == backup_setting::NOT_LOCKED) {
825                 $setting->set_value($value);
826             }
827         }
829         if (!$rc->execute_precheck()) {
830             $precheckresults = $rc->get_precheck_results();
831             if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
832                 if (empty($CFG->keeptempdirectoriesonbackup)) {
833                     fulldelete($backupbasepath);
834                 }
836                 $errorinfo = '';
838                 foreach ($precheckresults['errors'] as $error) {
839                     $errorinfo .= $error;
840                 }
842                 if (array_key_exists('warnings', $precheckresults)) {
843                     foreach ($precheckresults['warnings'] as $warning) {
844                         $errorinfo .= $warning;
845                     }
846                 }
848                 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
849             }
850         }
852         $rc->execute_plan();
853         $rc->destroy();
855         $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
856         $course->fullname = $params['fullname'];
857         $course->shortname = $params['shortname'];
858         $course->visible = $params['visible'];
860         // Set shortname and fullname back.
861         $DB->update_record('course', $course);
863         if (empty($CFG->keeptempdirectoriesonbackup)) {
864             fulldelete($backupbasepath);
865         }
867         // Delete the course backup file created by this WebService. Originally located in the course backups area.
868         $file->delete();
870         return array('id' => $course->id, 'shortname' => $course->shortname);
871     }
873     /**
874      * Returns description of method result value
875      *
876      * @return external_description
877      * @since Moodle 2.3
878      */
879     public static function duplicate_course_returns() {
880         return new external_single_structure(
881             array(
882                 'id'       => new external_value(PARAM_INT, 'course id'),
883                 'shortname' => new external_value(PARAM_TEXT, 'short name'),
884             )
885         );
886     }
888     /**
889      * Returns description of method parameters
890      *
891      * @return external_function_parameters
892      * @since Moodle 2.3
893      */
894     public static function get_categories_parameters() {
895         return new external_function_parameters(
896             array(
897                 'criteria' => new external_multiple_structure(
898                     new external_single_structure(
899                         array(
900                             'key' => new external_value(PARAM_ALPHA,
901                                          'The category column to search, expected keys (value format) are:'.
902                                          '"id" (int) the category id,'.
903                                          '"name" (string) the category name,'.
904                                          '"parent" (int) the parent category id,'.
905                                          '"idnumber" (string) category idnumber'.
906                                          ' - user must have \'moodle/category:manage\' to search on idnumber,'.
907                                          '"visible" (int) whether the category is visible or not'.
908                                          ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
909                                          '"theme" (string) category theme'.
910                                          ' - user must have \'moodle/category:manage\' to search on theme'),
911                             'value' => new external_value(PARAM_RAW, 'the value to match')
912                         )
913                     ), VALUE_DEFAULT, array()
914                 ),
915                 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
916                                           (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
917             )
918         );
919     }
921     /**
922      * Get categories
923      *
924      * @param array $criteria Criteria to match the results
925      * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
926      * @return array list of categories
927      * @since Moodle 2.3
928      */
929     public static function get_categories($criteria = array(), $addsubcategories = true) {
930         global $CFG, $DB;
931         require_once($CFG->dirroot . "/course/lib.php");
933         // Validate parameters.
934         $params = self::validate_parameters(self::get_categories_parameters(),
935                 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
937         // Retrieve the categories.
938         $categories = array();
939         if (!empty($params['criteria'])) {
941             $conditions = array();
942             $wheres = array();
943             foreach ($params['criteria'] as $crit) {
944                 $key = trim($crit['key']);
946                 // Trying to avoid duplicate keys.
947                 if (!isset($conditions[$key])) {
949                     $context = context_system::instance();
950                     $value = null;
951                     switch ($key) {
952                         case 'id':
953                             $value = clean_param($crit['value'], PARAM_INT);
954                             break;
956                         case 'idnumber':
957                             if (has_capability('moodle/category:manage', $context)) {
958                                 $value = clean_param($crit['value'], PARAM_RAW);
959                             } else {
960                                 // We must throw an exception.
961                                 // Otherwise the dev client would think no idnumber exists.
962                                 throw new moodle_exception('criteriaerror',
963                                         'webservice', '', null,
964                                         'You don\'t have the permissions to search on the "idnumber" field.');
965                             }
966                             break;
968                         case 'name':
969                             $value = clean_param($crit['value'], PARAM_TEXT);
970                             break;
972                         case 'parent':
973                             $value = clean_param($crit['value'], PARAM_INT);
974                             break;
976                         case 'visible':
977                             if (has_capability('moodle/category:manage', $context)
978                                 or has_capability('moodle/category:viewhiddencategories',
979                                         context_system::instance())) {
980                                 $value = clean_param($crit['value'], PARAM_INT);
981                             } else {
982                                 throw new moodle_exception('criteriaerror',
983                                         'webservice', '', null,
984                                         'You don\'t have the permissions to search on the "visible" field.');
985                             }
986                             break;
988                         case 'theme':
989                             if (has_capability('moodle/category:manage', $context)) {
990                                 $value = clean_param($crit['value'], PARAM_THEME);
991                             } else {
992                                 throw new moodle_exception('criteriaerror',
993                                         'webservice', '', null,
994                                         'You don\'t have the permissions to search on the "theme" field.');
995                             }
996                             break;
998                         default:
999                             throw new moodle_exception('criteriaerror',
1000                                     'webservice', '', null,
1001                                     'You can not search on this criteria: ' . $key);
1002                     }
1004                     if (isset($value)) {
1005                         $conditions[$key] = $crit['value'];
1006                         $wheres[] = $key . " = :" . $key;
1007                     }
1008                 }
1009             }
1011             if (!empty($wheres)) {
1012                 $wheres = implode(" AND ", $wheres);
1014                 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1016                 // Retrieve its sub subcategories (all levels).
1017                 if ($categories and !empty($params['addsubcategories'])) {
1018                     $newcategories = array();
1020                     foreach ($categories as $category) {
1021                         $sqllike = $DB->sql_like('path', ':path');
1022                         $sqlparams = array('path' => $category->path.'/%'); // It will NOT include the specified category.
1023                         $subcategories = $DB->get_records_select('course_categories', $sqllike, $sqlparams);
1024                         $newcategories = $newcategories + $subcategories;   // Both arrays have integer as keys.
1025                     }
1026                     $categories = $categories + $newcategories;
1027                 }
1028             }
1030         } else {
1031             // Retrieve all categories in the database.
1032             $categories = $DB->get_records('course_categories');
1033         }
1035         // The not returned categories. key => category id, value => reason of exclusion.
1036         $excludedcats = array();
1038         // The returned categories.
1039         $categoriesinfo = array();
1041         // We need to sort the categories by path.
1042         // The parent cats need to be checked by the algo first.
1043         usort($categories, "core_course_external::compare_categories_by_path");
1045         foreach ($categories as $category) {
1047             // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1048             $parents = explode('/', $category->path);
1049             unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1050             foreach ($parents as $parentid) {
1051                 // Note: when the parent exclusion was due to the context,
1052                 // the sub category could still be returned.
1053                 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1054                     $excludedcats[$category->id] = 'parent';
1055                 }
1056             }
1058             // Check category depth is <= maxdepth (do not check for user who can manage categories).
1059             if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
1060                     and !has_capability('moodle/category:manage', $context)) {
1061                 $excludedcats[$category->id] = 'depth';
1062             }
1064             // Check the user can use the category context.
1065             $context = context_coursecat::instance($category->id);
1066             try {
1067                 self::validate_context($context);
1068             } catch (Exception $e) {
1069                 $excludedcats[$category->id] = 'context';
1071                 // If it was the requested category then throw an exception.
1072                 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1073                     $exceptionparam = new stdClass();
1074                     $exceptionparam->message = $e->getMessage();
1075                     $exceptionparam->catid = $category->id;
1076                     throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1077                 }
1078             }
1080             // Return the category information.
1081             if (!isset($excludedcats[$category->id])) {
1083                 // Final check to see if the category is visible to the user.
1084                 if ($category->visible
1085                         or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1086                         or has_capability('moodle/category:manage', $context)) {
1088                     $categoryinfo = array();
1089                     $categoryinfo['id'] = $category->id;
1090                     $categoryinfo['name'] = $category->name;
1091                     list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1092                         external_format_text($category->description, $category->descriptionformat,
1093                                 $context->id, 'coursecat', 'description', null);
1094                     $categoryinfo['parent'] = $category->parent;
1095                     $categoryinfo['sortorder'] = $category->sortorder;
1096                     $categoryinfo['coursecount'] = $category->coursecount;
1097                     $categoryinfo['depth'] = $category->depth;
1098                     $categoryinfo['path'] = $category->path;
1100                     // Some fields only returned for admin.
1101                     if (has_capability('moodle/category:manage', $context)) {
1102                         $categoryinfo['idnumber'] = $category->idnumber;
1103                         $categoryinfo['visible'] = $category->visible;
1104                         $categoryinfo['visibleold'] = $category->visibleold;
1105                         $categoryinfo['timemodified'] = $category->timemodified;
1106                         $categoryinfo['theme'] = $category->theme;
1107                     }
1109                     $categoriesinfo[] = $categoryinfo;
1110                 } else {
1111                     $excludedcats[$category->id] = 'visibility';
1112                 }
1113             }
1114         }
1116         // Sorting the resulting array so it looks a bit better for the client developer.
1117         usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
1119         return $categoriesinfo;
1120     }
1122     /**
1123      * Sort categories array by path
1124      * private function: only used by get_categories
1125      *
1126      * @param array $category1
1127      * @param array $category2
1128      * @return int result of strcmp
1129      * @since Moodle 2.3
1130      */
1131     private static function compare_categories_by_path($category1, $category2) {
1132         return strcmp($category1->path, $category2->path);
1133     }
1135     /**
1136      * Sort categories array by sortorder
1137      * private function: only used by get_categories
1138      *
1139      * @param array $category1
1140      * @param array $category2
1141      * @return int result of strcmp
1142      * @since Moodle 2.3
1143      */
1144     private static function compare_categories_by_sortorder($category1, $category2) {
1145         return strcmp($category1['sortorder'], $category2['sortorder']);
1146     }
1148     /**
1149      * Returns description of method result value
1150      *
1151      * @return external_description
1152      * @since Moodle 2.3
1153      */
1154     public static function get_categories_returns() {
1155         return new external_multiple_structure(
1156             new external_single_structure(
1157                 array(
1158                     'id' => new external_value(PARAM_INT, 'category id'),
1159                     'name' => new external_value(PARAM_TEXT, 'category name'),
1160                     'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1161                     'description' => new external_value(PARAM_RAW, 'category description'),
1162                     'descriptionformat' => new external_format_value('description'),
1163                     'parent' => new external_value(PARAM_INT, 'parent category id'),
1164                     'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1165                     'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1166                     'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1167                     'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1168                     'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1169                     'depth' => new external_value(PARAM_INT, 'category depth'),
1170                     'path' => new external_value(PARAM_TEXT, 'category path'),
1171                     'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1172                 ), 'List of categories'
1173             )
1174         );
1175     }
1177     /**
1178      * Returns description of method parameters
1179      *
1180      * @return external_function_parameters
1181      * @since Moodle 2.3
1182      */
1183     public static function create_categories_parameters() {
1184         return new external_function_parameters(
1185             array(
1186                 'categories' => new external_multiple_structure(
1187                         new external_single_structure(
1188                             array(
1189                                 'name' => new external_value(PARAM_TEXT, 'new category name'),
1190                                 'parent' => new external_value(PARAM_INT,
1191                                         'the parent category id inside which the new category will be created
1192                                          - set to 0 for a root category',
1193                                         VALUE_DEFAULT, 0),
1194                                 'idnumber' => new external_value(PARAM_RAW,
1195                                         'the new category idnumber', VALUE_OPTIONAL),
1196                                 'description' => new external_value(PARAM_RAW,
1197                                         'the new category description', VALUE_OPTIONAL),
1198                                 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1199                                 'theme' => new external_value(PARAM_THEME,
1200                                         'the new category theme. This option must be enabled on moodle',
1201                                         VALUE_OPTIONAL),
1202                         )
1203                     )
1204                 )
1205             )
1206         );
1207     }
1209     /**
1210      * Create categories
1211      *
1212      * @param array $categories - see create_categories_parameters() for the array structure
1213      * @return array - see create_categories_returns() for the array structure
1214      * @since Moodle 2.3
1215      */
1216     public static function create_categories($categories) {
1217         global $CFG, $DB;
1218         require_once($CFG->dirroot . "/course/lib.php");
1220         $params = self::validate_parameters(self::create_categories_parameters(),
1221                         array('categories' => $categories));
1223         $transaction = $DB->start_delegated_transaction();
1225         $createdcategories = array();
1226         foreach ($params['categories'] as $category) {
1227             if ($category['parent']) {
1228                 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1229                     throw new moodle_exception('unknowcategory');
1230                 }
1231                 $context = context_coursecat::instance($category['parent']);
1232             } else {
1233                 $context = context_system::instance();
1234             }
1235             self::validate_context($context);
1236             require_capability('moodle/category:manage', $context);
1238             // Check name.
1239             if (textlib::strlen($category['name'])>255) {
1240                 throw new moodle_exception('categorytoolong');
1241             }
1243             $newcategory = new stdClass();
1244             $newcategory->name = $category['name'];
1245             $newcategory->parent = $category['parent'];
1246             $newcategory->sortorder = 999; // Same as in the course/editcategory.php .
1247             // Format the description.
1248             if (!empty($category['description'])) {
1249                 $newcategory->description = $category['description'];
1250             }
1251             $newcategory->descriptionformat = external_validate_format($category['descriptionformat']);
1252             if (isset($category['theme']) and !empty($CFG->allowcategorythemes)) {
1253                 $newcategory->theme = $category['theme'];
1254             }
1255             // Check id number.
1256             if (!empty($category['idnumber'])) { // Same as in course/editcategory_form.php .
1257                 if (textlib::strlen($category['idnumber'])>100) {
1258                     throw new moodle_exception('idnumbertoolong');
1259                 }
1260                 if ($existing = $DB->get_record('course_categories', array('idnumber' => $category['idnumber']))) {
1261                     if ($existing->id) {
1262                         throw new moodle_exception('idnumbertaken');
1263                     }
1264                 }
1265                 $newcategory->idnumber = $category['idnumber'];
1266             }
1268             $newcategory = create_course_category($newcategory);
1269             // Populate special fields.
1270             fix_course_sortorder();
1272             $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
1273         }
1275         $transaction->allow_commit();
1277         return $createdcategories;
1278     }
1280     /**
1281      * Returns description of method parameters
1282      *
1283      * @return external_function_parameters
1284      * @since Moodle 2.3
1285      */
1286     public static function create_categories_returns() {
1287         return new external_multiple_structure(
1288             new external_single_structure(
1289                 array(
1290                     'id' => new external_value(PARAM_INT, 'new category id'),
1291                     'name' => new external_value(PARAM_TEXT, 'new category name'),
1292                 )
1293             )
1294         );
1295     }
1297     /**
1298      * Returns description of method parameters
1299      *
1300      * @return external_function_parameters
1301      * @since Moodle 2.3
1302      */
1303     public static function update_categories_parameters() {
1304         return new external_function_parameters(
1305             array(
1306                 'categories' => new external_multiple_structure(
1307                     new external_single_structure(
1308                         array(
1309                             'id'       => new external_value(PARAM_INT, 'course id'),
1310                             'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1311                             'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1312                             'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1313                             'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
1314                             'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
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 = external_validate_format($cat['descriptionformat']);
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         $transaction = $DB->start_delegated_transaction();
1436         foreach ($params['categories'] as $category) {
1437             if (!$deletecat = $DB->get_record('course_categories', array('id' => $category['id']))) {
1438                 throw new moodle_exception('unknowcategory');
1439             }
1440             $context = context_coursecat::instance($deletecat->id);
1441             require_capability('moodle/category:manage', $context);
1442             self::validate_context($context);
1443             self::validate_context(get_category_or_system_context($deletecat->parent));
1445             if ($category['recursive']) {
1446                 // If recursive was specified, then we recursively delete the category's contents.
1447                 category_delete_full($deletecat, false);
1448             } else {
1449                 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1450                 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1451                 // We must move to an existing category.
1452                 if (!empty($category['newparent'])) {
1453                     if (!$DB->record_exists('course_categories', array('id' => $category['newparent']))) {
1454                         throw new moodle_exception('unknowcategory');
1455                     }
1456                     $newparent = $category['newparent'];
1457                 } else {
1458                     $newparent = $deletecat->parent;
1459                 }
1461                 // This operation is not allowed. We must move contents to an existing category.
1462                 if ($newparent == 0) {
1463                     throw new moodle_exception('movecatcontentstoroot');
1464                 }
1466                 $parentcontext = get_category_or_system_context($newparent);
1467                 require_capability('moodle/category:manage', $parentcontext);
1468                 self::validate_context($parentcontext);
1469                 category_delete_move($deletecat, $newparent, false);
1470             }
1471         }
1473         $transaction->allow_commit();
1474     }
1476     /**
1477      * Returns description of method parameters
1478      *
1479      * @return external_function_parameters
1480      * @since Moodle 2.3
1481      */
1482     public static function delete_categories_returns() {
1483         return null;
1484     }
1488 /**
1489  * Deprecated course external functions
1490  *
1491  * @package    core_course
1492  * @copyright  2009 Petr Skodak
1493  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1494  * @since Moodle 2.0
1495  * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
1496  * @todo MDL-31194 This will be deleted in Moodle 2.5.
1497  * @see core_course_external
1498  */
1499 class moodle_course_external extends external_api {
1501     /**
1502      * Returns description of method parameters
1503      *
1504      * @return external_function_parameters
1505      * @since Moodle 2.0
1506      * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1507      * @todo MDL-31194 This will be deleted in Moodle 2.5.
1508      * @see core_course_external::get_courses_parameters()
1509      */
1510     public static function get_courses_parameters() {
1511         return core_course_external::get_courses_parameters();
1512     }
1514     /**
1515      * Get courses
1516      *
1517      * @param array $options
1518      * @return array
1519      * @since Moodle 2.0
1520      * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1521      * @todo MDL-31194 This will be deleted in Moodle 2.5.
1522      * @see core_course_external::get_courses()
1523      */
1524     public static function get_courses($options) {
1525         return core_course_external::get_courses($options);
1526     }
1528     /**
1529      * Returns description of method result value
1530      *
1531      * @return external_description
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_returns()
1536      */
1537     public static function get_courses_returns() {
1538         return core_course_external::get_courses_returns();
1539     }
1541     /**
1542      * Returns description of method parameters
1543      *
1544      * @return external_function_parameters
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::create_courses_parameters()
1549      */
1550     public static function create_courses_parameters() {
1551         return core_course_external::create_courses_parameters();
1552     }
1554     /**
1555      * Create  courses
1556      *
1557      * @param array $courses
1558      * @return array courses (id and shortname only)
1559      * @since Moodle 2.0
1560      * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1561      * @todo MDL-31194 This will be deleted in Moodle 2.5.
1562      * @see core_course_external::create_courses()
1563      */
1564     public static function create_courses($courses) {
1565         return core_course_external::create_courses($courses);
1566     }
1568     /**
1569      * Returns description of method result value
1570      *
1571      * @return external_description
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_returns()
1576      */
1577     public static function create_courses_returns() {
1578         return core_course_external::create_courses_returns();
1579     }