externallib.php 80.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

17

18
/**
19
 * External course API
20
 *
21
22
23
 * @package    core_course
 * @category   external
 * @copyright  2009 Petr Skodak
24
25
26
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

27
28
defined('MOODLE_INTERNAL') || die;

29
30
require_once("$CFG->libdir/externallib.php");

31
/**
32
33
34
35
36
37
38
 * Course external functions
 *
 * @package    core_course
 * @category   external
 * @copyright  2011 Jerome Mouneyrac
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 * @since Moodle 2.2
39
40
 */
class core_course_external extends external_api {
41

42
43
    /**
     * Returns description of method parameters
44
     *
45
     * @return external_function_parameters
46
     * @since Moodle 2.2
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
     */
    public static function get_course_contents_parameters() {
        return new external_function_parameters(
                array('courseid' => new external_value(PARAM_INT, 'course id'),
                      'options' => new external_multiple_structure (
                              new external_single_structure(
                                    array('name' => new external_value(PARAM_ALPHANUM, 'option name'),
                                          'value' => new external_value(PARAM_RAW, 'the value of the option, this param is personaly validated in the external function.')
                              )
                      ), 'Options, not used yet, might be used in later version', VALUE_DEFAULT, array())
                )
        );
    }

    /**
     * Get course contents
63
64
65
     *
     * @param int $courseid course id
     * @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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
        global $CFG, $DB;
        require_once($CFG->dirroot . "/course/lib.php");

        //validate parameter
        $params = self::validate_parameters(self::get_course_contents_parameters(),
                        array('courseid' => $courseid, 'options' => $options));

        //retrieve the course
        $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);

        //check course format exist
        if (!file_exists($CFG->dirroot . '/course/format/' . $course->format . '/lib.php')) {
            throw new moodle_exception('cannotgetcoursecontents', 'webservice', '', null, get_string('courseformatnotfound', 'error', '', $course->format));
        } else {
            require_once($CFG->dirroot . '/course/format/' . $course->format . '/lib.php');
        }

        // now security checks
88
        $context = context_course::instance($course->id, IGNORE_MISSING);
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
        try {
            self::validate_context($context);
        } catch (Exception $e) {
            $exceptionparam = new stdClass();
            $exceptionparam->message = $e->getMessage();
            $exceptionparam->courseid = $course->id;
            throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
        }

        $canupdatecourse = has_capability('moodle/course:update', $context);

        //create return value
        $coursecontents = array();

        if ($canupdatecourse or $course->visible
                or has_capability('moodle/course:viewhiddencourses', $context)) {

            //retrieve sections
            $modinfo = get_fast_modinfo($course);
108
            $sections = $modinfo->get_section_info_all();
109
110
111
112

            //for each sections (first displayed to last displayed)
            foreach ($sections as $key => $section) {

113
                if (!$section->uservisible) {
114
115
116
117
118
119
120
121
                    continue;
                }

                // reset $sectioncontents
                $sectionvalues = array();
                $sectionvalues['id'] = $section->id;
                $sectionvalues['name'] = get_section_name($course, $section);
                $sectionvalues['visible'] = $section->visible;
122
123
124
                list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
                        external_format_text($section->summary, $section->summaryformat,
                                $context->id, 'course', 'section', $section->id);
125
126
127
                $sectioncontents = array();

                //for each module of the section
128
                foreach ($modinfo->sections[$section->section] as $cmid) {
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
                    $cm = $modinfo->cms[$cmid];

                    // stop here if the module is not visible to the user
                    if (!$cm->uservisible) {
                        continue;
                    }

                    $module = array();

                    //common info (for people being able to see the module or availability dates)
                    $module['id'] = $cm->id;
                    $module['name'] = format_string($cm->name, true);
                    $module['modname'] = $cm->modname;
                    $module['modplural'] = $cm->modplural;
                    $module['modicon'] = $cm->get_icon_url()->out(false);
                    $module['indent'] = $cm->indent;

146
                    $modcontext = context_module::instance($cm->id);
147
148
149
150
151
152
153
154
155
156
157
158

                    if (!empty($cm->showdescription)) {
                        $module['description'] = $cm->get_content();
                    }

                    //url of the module
                    $url = $cm->get_url();
                    if ($url) { //labels don't have url
                        $module['url'] = $cm->get_url()->out();
                    }

                    $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
159
                                        context_module::instance($cm->id));
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
                    //user that can view hidden module should know about the visibility
                    $module['visible'] = $cm->visible;

                    //availability date (also send to user who can see hidden module when the showavailabilyt is ON)
                    if ($canupdatecourse or ($CFG->enableavailability && $canviewhidden && $cm->showavailability)) {
                        $module['availablefrom'] = $cm->availablefrom;
                        $module['availableuntil'] = $cm->availableuntil;
                    }

                    $baseurl = 'webservice/pluginfile.php';

                    //call $modulename_export_contents
                    //(each module callback take care about checking the capabilities)
                    require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
                    $getcontentfunction = $cm->modname.'_export_contents';
                    if (function_exists($getcontentfunction)) {
                        if ($contents = $getcontentfunction($cm, $baseurl)) {
                            $module['contents'] = $contents;
                        }
                    }

                    //assign result to $sectioncontents
                    $sectioncontents[] = $module;

                }
                $sectionvalues['modules'] = $sectioncontents;

                // assign result to $coursecontents
                $coursecontents[] = $sectionvalues;
            }
        }
        return $coursecontents;
    }

    /**
     * Returns description of method result value
196
     *
197
     * @return external_description
198
     * @since Moodle 2.2
199
200
201
202
203
204
205
206
207
     */
    public static function get_course_contents_returns() {
        return new external_multiple_structure(
            new external_single_structure(
                array(
                    'id' => new external_value(PARAM_INT, 'Section ID'),
                    'name' => new external_value(PARAM_TEXT, 'Section name'),
                    'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
                    'summary' => new external_value(PARAM_RAW, 'Section description'),
208
                    'summaryformat' => new external_format_value('summary'),
209
210
211
212
213
                    'modules' => new external_multiple_structure(
                            new external_single_structure(
                                array(
                                    'id' => new external_value(PARAM_INT, 'activity id'),
                                    'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
214
                                    'name' => new external_value(PARAM_RAW, 'activity module name'),
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
                                    'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
                                    'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
                                    'modicon' => new external_value(PARAM_URL, 'activity icon url'),
                                    'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
                                    'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
                                    'availablefrom' => new external_value(PARAM_INT, 'module availability start date', VALUE_OPTIONAL),
                                    'availableuntil' => new external_value(PARAM_INT, 'module availability en date', VALUE_OPTIONAL),
                                    'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
                                    'contents' => new external_multiple_structure(
                                          new external_single_structure(
                                              array(
                                                  // content info
                                                  'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
                                                  'filename'=> new external_value(PARAM_FILE, 'filename'),
                                                  'filepath'=> new external_value(PARAM_PATH, 'filepath'),
                                                  'filesize'=> new external_value(PARAM_INT, 'filesize'),
                                                  'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
                                                  'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
                                                  'timecreated' => new external_value(PARAM_INT, 'Time created'),
                                                  'timemodified' => new external_value(PARAM_INT, 'Time modified'),
                                                  'sortorder' => new external_value(PARAM_INT, 'Content sort order'),

                                                  // copyright related info
                                                  'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
                                                  'author' => new external_value(PARAM_TEXT, 'Content owner'),
                                                  'license' => new external_value(PARAM_TEXT, 'Content license'),
                                              )
                                          ), VALUE_DEFAULT, array()
                                      )
                                )
                            ), 'list of module'
                    )
                )
            )
        );
    }

252
253
    /**
     * Returns description of method parameters
254
     *
255
     * @return external_function_parameters
256
     * @since Moodle 2.3
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
     */
    public static function get_courses_parameters() {
        return new external_function_parameters(
                array('options' => new external_single_structure(
                            array('ids' => new external_multiple_structure(
                                        new external_value(PARAM_INT, 'Course id')
                                        , 'List of course id. If empty return all courses
                                            except front page course.',
                                        VALUE_OPTIONAL)
                            ), 'options - operator OR is used', VALUE_DEFAULT, array())
                )
        );
    }

    /**
     * Get courses
273
274
     *
     * @param array $options It contains an array (list of ids)
275
     * @return array
276
     * @since Moodle 2.2
277
     */
278
    public static function get_courses($options = array()) {
279
280
281
282
283
284
285
286
        global $CFG, $DB;
        require_once($CFG->dirroot . "/course/lib.php");

        //validate parameter
        $params = self::validate_parameters(self::get_courses_parameters(),
                        array('options' => $options));

        //retrieve courses
287
        if (!array_key_exists('ids', $params['options'])
288
289
290
291
292
293
294
295
296
297
298
                or empty($params['options']['ids'])) {
            $courses = $DB->get_records('course');
        } else {
            $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
        }

        //create return value
        $coursesinfo = array();
        foreach ($courses as $course) {

            // now security checks
299
            $context = context_course::instance($course->id, IGNORE_MISSING);
300
            $courseformatoptions = course_get_format($course)->get_format_options();
301
302
303
304
305
306
            try {
                self::validate_context($context);
            } catch (Exception $e) {
                $exceptionparam = new stdClass();
                $exceptionparam->message = $e->getMessage();
                $exceptionparam->courseid = $course->id;
307
                throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
308
309
310
311
312
313
314
315
            }
            require_capability('moodle/course:view', $context);

            $courseinfo = array();
            $courseinfo['id'] = $course->id;
            $courseinfo['fullname'] = $course->fullname;
            $courseinfo['shortname'] = $course->shortname;
            $courseinfo['categoryid'] = $course->category;
316
317
            list($courseinfo['summary'], $courseinfo['summaryformat']) =
                external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
318
319
            $courseinfo['format'] = $course->format;
            $courseinfo['startdate'] = $course->startdate;
320
321
322
323
            if (array_key_exists('numsections', $courseformatoptions)) {
                // For backward-compartibility
                $courseinfo['numsections'] = $courseformatoptions['numsections'];
            }
324
325
326
327
328
329
330
331
332
333
334

            //some field should be returned only if the user has update permission
            $courseadmin = has_capability('moodle/course:update', $context);
            if ($courseadmin) {
                $courseinfo['categorysortorder'] = $course->sortorder;
                $courseinfo['idnumber'] = $course->idnumber;
                $courseinfo['showgrades'] = $course->showgrades;
                $courseinfo['showreports'] = $course->showreports;
                $courseinfo['newsitems'] = $course->newsitems;
                $courseinfo['visible'] = $course->visible;
                $courseinfo['maxbytes'] = $course->maxbytes;
335
336
337
338
                if (array_key_exists('hiddensections', $courseformatoptions)) {
                    // For backward-compartibility
                    $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
                }
339
340
341
342
343
344
345
346
347
348
                $courseinfo['groupmode'] = $course->groupmode;
                $courseinfo['groupmodeforce'] = $course->groupmodeforce;
                $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
                $courseinfo['lang'] = $course->lang;
                $courseinfo['timecreated'] = $course->timecreated;
                $courseinfo['timemodified'] = $course->timemodified;
                $courseinfo['forcetheme'] = $course->theme;
                $courseinfo['enablecompletion'] = $course->enablecompletion;
                $courseinfo['completionstartonenrol'] = $course->completionstartonenrol;
                $courseinfo['completionnotify'] = $course->completionnotify;
349
                $courseinfo['courseformatoptions'] = array();
350
                foreach ($courseformatoptions as $key => $value) {
351
352
353
                    $courseinfo['courseformatoptions'][] = array(
                        'name' => $key,
                        'value' => $value
354
355
                    );
                }
356
357
358
359
360
361
362
363
364
365
366
367
368
            }

            if ($courseadmin or $course->visible
                    or has_capability('moodle/course:viewhiddencourses', $context)) {
                $coursesinfo[] = $courseinfo;
            }
        }

        return $coursesinfo;
    }

    /**
     * Returns description of method result value
369
     *
370
     * @return external_description
371
     * @since Moodle 2.2
372
373
374
375
376
377
378
379
380
381
382
383
384
     */
    public static function get_courses_returns() {
        return new external_multiple_structure(
                new external_single_structure(
                        array(
                            'id' => new external_value(PARAM_INT, 'course id'),
                            'shortname' => new external_value(PARAM_TEXT, 'course short name'),
                            'categoryid' => new external_value(PARAM_INT, 'category id'),
                            'categorysortorder' => new external_value(PARAM_INT,
                                    'sort order into the category', VALUE_OPTIONAL),
                            'fullname' => new external_value(PARAM_TEXT, 'full name'),
                            'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
                            'summary' => new external_value(PARAM_RAW, 'summary'),
385
                            'summaryformat' => new external_format_value('summary'),
386
                            'format' => new external_value(PARAM_PLUGIN,
387
388
389
390
391
392
393
                                    'course format: weeks, topics, social, site,..'),
                            'showgrades' => new external_value(PARAM_INT,
                                    '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
                            'newsitems' => new external_value(PARAM_INT,
                                    'number of recent items appearing on the course page', VALUE_OPTIONAL),
                            'startdate' => new external_value(PARAM_INT,
                                    'timestamp when the course start'),
394
                            'numsections' => new external_value(PARAM_INT,
395
                                    '(deprecated, use courseformatoptions) number of weeks/topics',
396
                                    VALUE_OPTIONAL),
397
398
399
400
401
402
403
404
                            'maxbytes' => new external_value(PARAM_INT,
                                    'largest size of file that can be uploaded into the course',
                                    VALUE_OPTIONAL),
                            'showreports' => new external_value(PARAM_INT,
                                    'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
                            'visible' => new external_value(PARAM_INT,
                                    '1: available to student, 0:not available', VALUE_OPTIONAL),
                            'hiddensections' => new external_value(PARAM_INT,
405
                                    '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
                                    VALUE_OPTIONAL),
                            'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
                                    VALUE_OPTIONAL),
                            'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
                                    VALUE_OPTIONAL),
                            'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
                                    VALUE_OPTIONAL),
                            'timecreated' => new external_value(PARAM_INT,
                                    'timestamp when the course have been created', VALUE_OPTIONAL),
                            'timemodified' => new external_value(PARAM_INT,
                                    'timestamp when the course have been modified', VALUE_OPTIONAL),
                            'enablecompletion' => new external_value(PARAM_INT,
                                    'Enabled, control via completion and activity settings. Disbaled,
                                        not shown in activity settings.',
                                    VALUE_OPTIONAL),
                            'completionstartonenrol' => new external_value(PARAM_INT,
                                    '1: begin tracking a student\'s progress in course completion
                                        after course enrolment. 0: does not',
                                    VALUE_OPTIONAL),
                            'completionnotify' => new external_value(PARAM_INT,
                                    '1: yes 0: no', VALUE_OPTIONAL),
                            'lang' => new external_value(PARAM_SAFEDIR,
                                    'forced course language', VALUE_OPTIONAL),
                            'forcetheme' => new external_value(PARAM_PLUGIN,
                                    'name of the force theme', VALUE_OPTIONAL),
431
                            'courseformatoptions' => new external_multiple_structure(
432
                                new external_single_structure(
433
434
                                    array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
                                        'value' => new external_value(PARAM_RAW, 'course format option value')
435
436
437
                                )),
                                    'additional options for particular course format', VALUE_OPTIONAL
                             ),
438
                        ), 'course'
439
440
441
442
                )
        );
    }

443
444
    /**
     * Returns description of method parameters
445
     *
446
     * @return external_function_parameters
447
     * @since Moodle 2.2
448
449
450
451
452
453
454
455
456
457
458
459
460
     */
    public static function create_courses_parameters() {
        $courseconfig = get_config('moodlecourse'); //needed for many default values
        return new external_function_parameters(
            array(
                'courses' => new external_multiple_structure(
                    new external_single_structure(
                        array(
                            'fullname' => new external_value(PARAM_TEXT, 'full name'),
                            'shortname' => new external_value(PARAM_TEXT, 'course short name'),
                            'categoryid' => new external_value(PARAM_INT, 'category id'),
                            'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
                            'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
461
                            'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
462
                            'format' => new external_value(PARAM_PLUGIN,
463
464
465
466
467
468
469
470
471
472
                                    'course format: weeks, topics, social, site,..',
                                    VALUE_DEFAULT, $courseconfig->format),
                            'showgrades' => new external_value(PARAM_INT,
                                    '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
                                    $courseconfig->showgrades),
                            'newsitems' => new external_value(PARAM_INT,
                                    'number of recent items appearing on the course page',
                                    VALUE_DEFAULT, $courseconfig->newsitems),
                            'startdate' => new external_value(PARAM_INT,
                                    'timestamp when the course start', VALUE_OPTIONAL),
473
                            'numsections' => new external_value(PARAM_INT,
474
                                    '(deprecated, use courseformatoptions) number of weeks/topics',
475
                                    VALUE_OPTIONAL),
476
477
478
479
480
481
482
483
484
                            'maxbytes' => new external_value(PARAM_INT,
                                    'largest size of file that can be uploaded into the course',
                                    VALUE_DEFAULT, $courseconfig->maxbytes),
                            'showreports' => new external_value(PARAM_INT,
                                    'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
                                    $courseconfig->showreports),
                            'visible' => new external_value(PARAM_INT,
                                    '1: available to student, 0:not available', VALUE_OPTIONAL),
                            'hiddensections' => new external_value(PARAM_INT,
485
                                    '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
486
                                    VALUE_OPTIONAL),
487
488
489
490
491
492
493
                            'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
                                    VALUE_DEFAULT, $courseconfig->groupmode),
                            'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
                                    VALUE_DEFAULT, $courseconfig->groupmodeforce),
                            'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
                                    VALUE_DEFAULT, 0),
                            'enablecompletion' => new external_value(PARAM_INT,
494
                                    'Enabled, control via completion and activity settings. Disabled,
495
496
497
498
499
500
501
502
                                        not shown in activity settings.',
                                    VALUE_OPTIONAL),
                            'completionstartonenrol' => new external_value(PARAM_INT,
                                    '1: begin tracking a student\'s progress in course completion after
                                        course enrolment. 0: does not',
                                    VALUE_OPTIONAL),
                            'completionnotify' => new external_value(PARAM_INT,
                                    '1: yes 0: no', VALUE_OPTIONAL),
503
                            'lang' => new external_value(PARAM_SAFEDIR,
504
                                    'forced course language', VALUE_OPTIONAL),
505
                            'forcetheme' => new external_value(PARAM_PLUGIN,
506
                                    'name of the force theme', VALUE_OPTIONAL),
507
                            'courseformatoptions' => new external_multiple_structure(
508
                                new external_single_structure(
509
510
                                    array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
                                        'value' => new external_value(PARAM_RAW, 'course format option value')
511
512
                                )),
                                    'additional options for particular course format', VALUE_OPTIONAL),
513
514
515
516
517
518
519
520
521
                        )
                    ), 'courses to create'
                )
            )
        );
    }

    /**
     * Create  courses
522
     *
523
524
     * @param array $courses
     * @return array courses (id and shortname only)
525
     * @since Moodle 2.2
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
     */
    public static function create_courses($courses) {
        global $CFG, $DB;
        require_once($CFG->dirroot . "/course/lib.php");
        require_once($CFG->libdir . '/completionlib.php');

        $params = self::validate_parameters(self::create_courses_parameters(),
                        array('courses' => $courses));

        $availablethemes = get_plugin_list('theme');
        $availablelangs = get_string_manager()->get_list_of_translations();

        $transaction = $DB->start_delegated_transaction();

        foreach ($params['courses'] as $course) {

            // Ensure the current user is allowed to run this function
543
            $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
544
545
546
547
548
549
            try {
                self::validate_context($context);
            } catch (Exception $e) {
                $exceptionparam = new stdClass();
                $exceptionparam->message = $e->getMessage();
                $exceptionparam->catid = $course['categoryid'];
550
                throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
551
552
553
554
            }
            require_capability('moodle/course:create', $context);

            // Make sure lang is valid
555
            if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
556
                throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
557
558
559
            }

            // Make sure theme is valid
560
            if (array_key_exists('forcetheme', $course)) {
561
562
                if (!empty($CFG->allowcoursethemes)) {
                    if (empty($availablethemes[$course['forcetheme']])) {
563
                        throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
564
565
566
567
568
569
570
571
572
573
574
575
576
                    } else {
                        $course['theme'] = $course['forcetheme'];
                    }
                }
            }

            //force visibility if ws user doesn't have the permission to set it
            $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
            if (!has_capability('moodle/course:visibility', $context)) {
                $course['visible'] = $category->visible;
            }

            //set default value for completion
577
            $courseconfig = get_config('moodlecourse');
578
            if (completion_info::is_enabled_for_site()) {
579
                if (!array_key_exists('enablecompletion', $course)) {
580
                    $course['enablecompletion'] = $courseconfig->enablecompletion;
581
                }
582
                if (!array_key_exists('completionstartonenrol', $course)) {
583
                    $course['completionstartonenrol'] = $courseconfig->completionstartonenrol;
584
585
586
587
588
589
590
591
                }
            } else {
                $course['enablecompletion'] = 0;
                $course['completionstartonenrol'] = 0;
            }

            $course['category'] = $course['categoryid'];

592
593
594
            // Summary format.
            $course['summaryformat'] = external_validate_format($course['summaryformat']);

595
596
597
            if (!empty($course['courseformatoptions'])) {
                foreach ($course['courseformatoptions'] as $option) {
                    $course[$option['name']] = $option['value'];
598
599
600
                }
            }

601
602
603
604
605
606
607
608
609
610
611
612
613
            //Note: create_course() core function check shortname, idnumber, category
            $course['id'] = create_course((object) $course)->id;

            $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
        }

        $transaction->allow_commit();

        return $resultcourses;
    }

    /**
     * Returns description of method result value
614
     *
615
     * @return external_description
616
     * @since Moodle 2.2
617
618
619
620
621
622
623
624
625
626
627
628
     */
    public static function create_courses_returns() {
        return new external_multiple_structure(
            new external_single_structure(
                array(
                    'id'       => new external_value(PARAM_INT, 'course id'),
                    'shortname' => new external_value(PARAM_TEXT, 'short name'),
                )
            )
        );
    }

629
630
    /**
     * Returns description of method parameters
631
     *
632
     * @return external_function_parameters
633
     * @since Moodle 2.2
634
635
636
637
638
639
640
641
642
643
644
     */
    public static function delete_courses_parameters() {
        return new external_function_parameters(
            array(
                'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
            )
        );
    }

    /**
     * Delete courses
645
     *
646
     * @param array $courseids A list of course ids
647
     * @since Moodle 2.2
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
     */
    public static function delete_courses($courseids) {
        global $CFG, $DB;
        require_once($CFG->dirroot."/course/lib.php");

        // Parameter validation.
        $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));

        $transaction = $DB->start_delegated_transaction();

        foreach ($params['courseids'] as $courseid) {
            $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);

            // Check if the context is valid.
            $coursecontext = context_course::instance($course->id);
            self::validate_context($coursecontext);

            // Check if the current user has enought permissions.
            if (!can_delete_course($courseid)) {
667
668
                throw new moodle_exception('cannotdeletecategorycourse', 'error',
                    '', format_string($course->fullname)." (id: $courseid)");
669
670
671
672
673
674
675
676
677
678
679
680
            }

            delete_course($course, false);
        }

        $transaction->allow_commit();

        return null;
    }

    /**
     * Returns description of method result value
681
     *
682
     * @return external_description
683
     * @since Moodle 2.2
684
685
686
687
688
     */
    public static function delete_courses_returns() {
        return null;
    }

689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
    /**
     * Returns description of method parameters
     *
     * @return external_function_parameters
     * @since Moodle 2.3
     */
    public static function duplicate_course_parameters() {
        return new external_function_parameters(
            array(
                'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
                'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
                'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
                'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
                'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
                'options' => new external_multiple_structure(
                    new external_single_structure(
                        array(
706
                                'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
707
708
709
710
711
712
713
714
715
716
717
718
                                            "activities" (int) Include course activites (default to 1 that is equal to yes),
                                            "blocks" (int) Include course blocks (default to 1 that is equal to yes),
                                            "filters" (int) Include course filters  (default to 1 that is equal to yes),
                                            "users" (int) Include users (default to 0 that is equal to no),
                                            "role_assignments" (int) Include role assignments  (default to 0 that is equal to no),
                                            "comments" (int) Include user comments  (default to 0 that is equal to no),
                                            "completion_information" (int) Include user course completion information  (default to 0 that is equal to no),
                                            "logs" (int) Include course logs  (default to 0 that is equal to no),
                                            "histories" (int) Include histories  (default to 0 that is equal to no)'
                                            ),
                                'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
                            )
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
                        )
                    ), VALUE_DEFAULT, array()
                ),
            )
        );
    }

    /**
     * Duplicate a course
     *
     * @param int $courseid
     * @param string $fullname Duplicated course fullname
     * @param string $shortname Duplicated course shortname
     * @param int $categoryid Duplicated course parent category id
     * @param int $visible Duplicated course availability
     * @param array $options List of backup options
     * @return array New course info
     * @since Moodle 2.3
     */
738
    public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
        global $CFG, $USER, $DB;
        require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
        require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');

        // Parameter validation.
        $params = self::validate_parameters(
                self::duplicate_course_parameters(),
                array(
                      'courseid' => $courseid,
                      'fullname' => $fullname,
                      'shortname' => $shortname,
                      'categoryid' => $categoryid,
                      'visible' => $visible,
                      'options' => $options
                )
        );

756
757
758
        // Context validation.

        if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
759
            throw new moodle_exception('invalidcourseid', 'error');
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
        }

        // Category where duplicated course is going to be created.
        $categorycontext = context_coursecat::instance($params['categoryid']);
        self::validate_context($categorycontext);

        // Course to be duplicated.
        $coursecontext = context_course::instance($course->id);
        self::validate_context($coursecontext);

        $backupdefaults = array(
            'activities' => 1,
            'blocks' => 1,
            'filters' => 1,
            'users' => 0,
            'role_assignments' => 0,
            'comments' => 0,
            'completion_information' => 0,
            'logs' => 0,
            'histories' => 0
        );

        $backupsettings = array();
        // Check for backup and restore options.
        if (!empty($params['options'])) {
            foreach ($params['options'] as $option) {

                // Strict check for a correct value (allways 1 or 0, true or false).
                $value = clean_param($option['value'], PARAM_INT);

                if ($value !== 0 and $value !== 1) {
                    throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
                }

                if (!isset($backupdefaults[$option['name']])) {
                    throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
                }

                $backupsettings[$option['name']] = $value;
            }
        }

        // Capability checking.

        // The backup controller check for this currently, this may be redundant.
        require_capability('moodle/course:create', $categorycontext);
        require_capability('moodle/restore:restorecourse', $categorycontext);
        require_capability('moodle/backup:backupcourse', $coursecontext);

        if (!empty($backupsettings['users'])) {
            require_capability('moodle/backup:userinfo', $coursecontext);
            require_capability('moodle/restore:userinfo', $categorycontext);
        }

        // Check if the shortname is used.
        if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
            foreach ($foundcourses as $foundcourse) {
                $foundcoursenames[] = $foundcourse->fullname;
            }

            $foundcoursenamestring = implode(',', $foundcoursenames);
            throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
        }

        // Backup the course.

        $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
        backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);

        foreach ($backupsettings as $name => $value) {
            $bc->get_plan()->get_setting($name)->set_value($value);
        }

        $backupid       = $bc->get_backupid();
        $backupbasepath = $bc->get_plan()->get_basepath();

        $bc->execute_plan();
        $results = $bc->get_results();
        $file = $results['backup_destination'];

        $bc->destroy();

        // Restore the backup immediately.

        // Check if we need to unzip the file because the backup temp dir does not contains backup files.
        if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
            $file->extract_to_pathname(get_file_packer(), $backupbasepath);
        }

        // Create new course.
        $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);

        $rc = new restore_controller($backupid, $newcourseid,
                backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);

        foreach ($backupsettings as $name => $value) {
            $setting = $rc->get_plan()->get_setting($name);
            if ($setting->get_status() == backup_setting::NOT_LOCKED) {
                $setting->set_value($value);
            }
        }

        if (!$rc->execute_precheck()) {
            $precheckresults = $rc->get_precheck_results();
            if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
                if (empty($CFG->keeptempdirectoriesonbackup)) {
                    fulldelete($backupbasepath);
                }

                $errorinfo = '';

                foreach ($precheckresults['errors'] as $error) {
                    $errorinfo .= $error;
                }

                if (array_key_exists('warnings', $precheckresults)) {
                    foreach ($precheckresults['warnings'] as $warning) {
                        $errorinfo .= $warning;
                    }
                }

                throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
            }
        }

        $rc->execute_plan();
        $rc->destroy();

        $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
        $course->fullname = $params['fullname'];
        $course->shortname = $params['shortname'];
        $course->visible = $params['visible'];

        // Set shortname and fullname back.
        $DB->update_record('course', $course);

        if (empty($CFG->keeptempdirectoriesonbackup)) {
            fulldelete($backupbasepath);
        }

        // Delete the course backup file created by this WebService. Originally located in the course backups area.
        $file->delete();

        return array('id' => $course->id, 'shortname' => $course->shortname);
    }

    /**
     * Returns description of method result value
     *
     * @return external_description
     * @since Moodle 2.3
     */
    public static function duplicate_course_returns() {
        return new external_single_structure(
            array(
                'id'       => new external_value(PARAM_INT, 'course id'),
                'shortname' => new external_value(PARAM_TEXT, 'short name'),
            )
        );
    }

921
    /**
922
     * Returns description of method parameters for import_course
923
924
     *
     * @return external_function_parameters
925
     * @since Moodle 2.4
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
     */
    public static function import_course_parameters() {
        return new external_function_parameters(
            array(
                'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
                'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
                'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
                'options' => new external_multiple_structure(
                    new external_single_structure(
                        array(
                                'name' => new external_value(PARAM_ALPHA, 'The backup option name:
                                            "activities" (int) Include course activites (default to 1 that is equal to yes),
                                            "blocks" (int) Include course blocks (default to 1 that is equal to yes),
                                            "filters" (int) Include course filters  (default to 1 that is equal to yes)'
                                            ),
                                'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
                            )
                        )
                    ), VALUE_DEFAULT, array()
                ),
            )
        );
    }

    /**
     * Imports a course
     *
     * @param int $importfrom The id of the course we are importing from
     * @param int $importto The id of the course we are importing to
     * @param bool $deletecontent Whether to delete the course we are importing to content
     * @param array $options List of backup options
     * @return null
958
     * @since Moodle 2.4
959
     */
960
    public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
961
962
963
964
965
966
        global $CFG, $USER, $DB;
        require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
        require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');

        // Parameter validation.
        $params = self::validate_parameters(
967
968
969
970
971
972
973
            self::import_course_parameters(),
            array(
                'importfrom' => $importfrom,
                'importto' => $importto,
                'deletecontent' => $deletecontent,
                'options' => $options
            )
974
975
976
977
978
979
980
981
982
        );

        if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
            throw new moodle_exception('invalidextparam', 'webservice', '', $option['deletecontent']);
        }

        // Context validation.

        if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
983
            throw new moodle_exception('invalidcourseid', 'error');
984
985
986
        }

        if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
987
            throw new moodle_exception('invalidcourseid', 'error');
988
989
990
991
992
993
994
995
996
        }

        $importfromcontext = context_course::instance($importfrom->id);
        self::validate_context($importfromcontext);

        $importtocontext = context_course::instance($importto->id);
        self::validate_context($importtocontext);

        $backupdefaults = array(
997
998
999
            'activities' => 1,
            'blocks' => 1,
            'filters' => 1
1000
        );
For faster browsing, not all history is shown. View entire blame