Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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/>.
16
 
17
 
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
 */
26
 
27
defined('MOODLE_INTERNAL') || die;
28
 
29
use core_course\external\course_summary_exporter;
30
use core_external\external_api;
31
use core_external\external_description;
32
use core_external\external_files;
33
use core_external\external_format_value;
34
use core_external\external_function_parameters;
35
use core_external\external_multiple_structure;
36
use core_external\external_single_structure;
37
use core_external\external_value;
38
use core_external\external_warnings;
39
use core_external\util;
40
require_once(__DIR__ . "/lib.php");
41
 
42
/**
43
 * Course external functions
44
 *
45
 * @package    core_course
46
 * @category   external
47
 * @copyright  2011 Jerome Mouneyrac
48
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
49
 * @since Moodle 2.2
50
 */
51
class core_course_external extends external_api {
52
 
53
    /**
54
     * Returns description of method parameters
55
     *
56
     * @return external_function_parameters
57
     * @since Moodle 2.9 Options available
58
     * @since Moodle 2.2
59
     */
60
    public static function get_course_contents_parameters() {
61
        return new external_function_parameters(
62
                array('courseid' => new external_value(PARAM_INT, 'course id'),
63
                      'options' => new external_multiple_structure (
64
                              new external_single_structure(
65
                                array(
66
                                    'name' => new external_value(PARAM_ALPHANUM,
67
                                                'The expected keys (value format) are:
68
                                                excludemodules (bool) Do not return modules, return only the sections structure
69
                                                excludecontents (bool) Do not return module contents (i.e: files inside a resource)
70
                                                includestealthmodules (bool) Return stealth modules for students in a special
71
                                                    section (with id -1)
72
                                                sectionid (int) Return only this section
73
                                                sectionnumber (int) Return only this section with number (order)
74
                                                cmid (int) Return only this module information (among the whole sections structure)
75
                                                modname (string) Return only modules with this name "label, forum, etc..."
76
                                                modid (int) Return only the module with this id (to be used with modname'),
77
                                    'value' => new external_value(PARAM_RAW, 'the value of the option,
78
                                                                    this param is personaly validated in the external function.')
79
                              )
80
                      ), 'Options, used since Moodle 2.9', VALUE_DEFAULT, array())
81
                )
82
        );
83
    }
84
 
85
    /**
86
     * Get course contents
87
     *
88
     * @param int $courseid course id
89
     * @param array $options Options for filtering the results, used since Moodle 2.9
90
     * @return array
91
     * @since Moodle 2.9 Options available
92
     * @since Moodle 2.2
93
     */
94
    public static function get_course_contents($courseid, $options = array()) {
95
        global $CFG, $DB, $USER, $PAGE;
96
        require_once($CFG->dirroot . "/course/lib.php");
97
        require_once($CFG->libdir . '/completionlib.php');
98
 
99
        //validate parameter
100
        $params = self::validate_parameters(self::get_course_contents_parameters(),
101
                        array('courseid' => $courseid, 'options' => $options));
102
 
103
        $filters = array();
104
        if (!empty($params['options'])) {
105
 
106
            foreach ($params['options'] as $option) {
107
                $name = trim($option['name']);
108
                // Avoid duplicated options.
109
                if (!isset($filters[$name])) {
110
                    switch ($name) {
111
                        case 'excludemodules':
112
                        case 'excludecontents':
113
                        case 'includestealthmodules':
114
                            $value = clean_param($option['value'], PARAM_BOOL);
115
                            $filters[$name] = $value;
116
                            break;
117
                        case 'sectionid':
118
                        case 'sectionnumber':
119
                        case 'cmid':
120
                        case 'modid':
121
                            $value = clean_param($option['value'], PARAM_INT);
122
                            if (is_numeric($value)) {
123
                                $filters[$name] = $value;
124
                            } else {
125
                                throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
126
                            }
127
                            break;
128
                        case 'modname':
129
                            $value = clean_param($option['value'], PARAM_PLUGIN);
130
                            if ($value) {
131
                                $filters[$name] = $value;
132
                            } else {
133
                                throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
134
                            }
135
                            break;
136
                        default:
137
                            throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
138
                    }
139
                }
140
            }
141
        }
142
 
143
        //retrieve the course
144
        $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
145
 
146
        // now security checks
147
        $context = context_course::instance($course->id, IGNORE_MISSING);
148
        try {
149
            self::validate_context($context);
150
        } catch (Exception $e) {
151
            $exceptionparam = new stdClass();
152
            $exceptionparam->message = $e->getMessage();
153
            $exceptionparam->courseid = $course->id;
154
            throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
155
        }
156
 
157
        $canupdatecourse = has_capability('moodle/course:update', $context);
158
 
159
        //create return value
160
        $coursecontents = array();
161
 
162
        if ($canupdatecourse or $course->visible
163
                or has_capability('moodle/course:viewhiddencourses', $context)) {
164
 
165
            //retrieve sections
166
            $modinfo = get_fast_modinfo($course);
167
            $sections = $modinfo->get_section_info_all();
168
            $courseformat = course_get_format($course);
169
            $coursenumsections = $courseformat->get_last_section_number();
170
            $stealthmodules = array();   // Array to keep all the modules available but not visible in a course section/topic.
171
 
172
            $completioninfo = new completion_info($course);
173
 
174
            //for each sections (first displayed to last displayed)
175
            $modinfosections = $modinfo->get_sections();
176
            foreach ($sections as $key => $section) {
177
 
178
                // This becomes true when we are filtering and we found the value to filter with.
179
                $sectionfound = false;
180
 
181
                // Filter by section id.
182
                if (!empty($filters['sectionid'])) {
183
                    if ($section->id != $filters['sectionid']) {
184
                        continue;
185
                    } else {
186
                        $sectionfound = true;
187
                    }
188
                }
189
 
190
                // Filter by section number. Note that 0 is a valid section number.
191
                if (isset($filters['sectionnumber'])) {
192
                    if ($key != $filters['sectionnumber']) {
193
                        continue;
194
                    } else {
195
                        $sectionfound = true;
196
                    }
197
                }
198
 
199
                // reset $sectioncontents
200
                $sectionvalues = array();
201
                $sectionvalues['id'] = $section->id;
202
                $sectionvalues['name'] = get_section_name($course, $section);
1441 ariadna 203
                // Temporary hack to be able to hide the subsections in certain app versions.
204
                if (!empty($section->component) && \core_useragent::is_moodle_app()) {
205
                    $sectionvalues['name'] = html_writer::span($sectionvalues['name'], 'course-' . $section->component);
206
                }
1 efrain 207
                $sectionvalues['visible'] = $section->visible;
208
 
209
                $options = (object) array('noclean' => true);
210
                list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
211
                        \core_external\util::format_text($section->summary, $section->summaryformat,
212
                                $context, 'course', 'section', $section->id, $options);
213
                $sectionvalues['section'] = $section->section;
214
                $sectionvalues['hiddenbynumsections'] = $section->section > $coursenumsections ? 1 : 0;
215
                $sectionvalues['uservisible'] = $section->uservisible;
216
                if (!empty($section->availableinfo)) {
217
                    $sectionvalues['availabilityinfo'] = \core_availability\info::format_info($section->availableinfo, $course);
218
                }
1441 ariadna 219
                $sectionvalues['component'] = $section->component;
220
                $sectionvalues['itemid'] = $section->itemid;
1 efrain 221
 
222
                $sectioncontents = array();
223
 
224
                // For each module of the section.
225
                if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) {
226
                    foreach ($modinfosections[$section->section] as $cmid) {
227
                        $cm = $modinfo->cms[$cmid];
228
                        $cminfo = cm_info::create($cm);
229
                        $activitydates = \core\activity_dates::get_dates_for_module($cminfo, $USER->id);
230
 
231
                        // Stop here if the module is not visible to the user on the course main page:
232
                        // The user can't access the module and the user can't view the module on the course page.
233
                        if (!$cm->uservisible && !$cm->is_visible_on_course_page()) {
234
                            continue;
235
                        }
236
 
237
                        // This becomes true when we are filtering and we found the value to filter with.
238
                        $modfound = false;
239
 
240
                        // Filter by cmid.
241
                        if (!empty($filters['cmid'])) {
242
                            if ($cmid != $filters['cmid']) {
243
                                continue;
244
                            } else {
245
                                $modfound = true;
246
                            }
247
                        }
248
 
249
                        // Filter by module name and id.
250
                        if (!empty($filters['modname'])) {
251
                            if ($cm->modname != $filters['modname']) {
252
                                continue;
253
                            } else if (!empty($filters['modid'])) {
254
                                if ($cm->instance != $filters['modid']) {
255
                                    continue;
256
                                } else {
257
                                    // Note that if we are only filtering by modname we don't break the loop.
258
                                    $modfound = true;
259
                                }
260
                            }
261
                        }
262
 
263
                        $module = array();
264
 
265
                        $modcontext = context_module::instance($cm->id);
266
 
267
                        $isbranded = component_callback('mod_' . $cm->modname, 'is_branded', [], false);
268
 
269
                        // Common info (for people being able to see the module or availability dates).
270
                        $module['id'] = $cm->id;
271
                        $module['name'] = \core_external\util::format_string($cm->name, $modcontext);
272
                        $module['instance'] = $cm->instance;
273
                        $module['contextid'] = $modcontext->id;
274
                        $module['modname'] = (string) $cm->modname;
275
                        $module['modplural'] = (string) $cm->modplural;
276
                        $module['modicon'] = $cm->get_icon_url()->out(false);
277
                        $module['purpose'] = plugin_supports('mod', $cm->modname, FEATURE_MOD_PURPOSE, MOD_PURPOSE_OTHER);
278
                        $module['branded'] = $isbranded;
279
                        $module['indent'] = $cm->indent;
280
                        $module['onclick'] = $cm->onclick;
281
                        $module['afterlink'] = $cm->afterlink;
282
                        $activitybadgedata = $cm->get_activitybadge();
283
                        if (!empty($activitybadgedata)) {
284
                            $module['activitybadge'] = $activitybadgedata;
285
                        }
286
                        $module['customdata'] = json_encode($cm->customdata);
287
                        $module['completion'] = $cm->completion;
288
                        $module['downloadcontent'] = $cm->downloadcontent;
289
                        $module['noviewlink'] = plugin_supports('mod', $cm->modname, FEATURE_NO_VIEW_LINK, false);
290
                        $module['dates'] = $activitydates;
291
                        $module['groupmode'] = $cm->groupmode;
292
 
293
                        // Check module completion.
294
                        $completion = $completioninfo->is_enabled($cm);
295
                        if ($completion != COMPLETION_DISABLED) {
296
                            $exporter = new \core_completion\external\completion_info_exporter($course, $cm, $USER->id);
297
                            $renderer = $PAGE->get_renderer('core');
298
                            $modulecompletiondata = (array)$exporter->export($renderer);
299
                            $module['completiondata'] = $modulecompletiondata;
300
                        }
301
 
302
                        if (!empty($cm->showdescription) or $module['noviewlink']) {
303
                            // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML.
304
                            $options = array('noclean' => true);
305
                            list($module['description'], $descriptionformat) = \core_external\util::format_text($cm->content,
306
                                FORMAT_HTML, $modcontext, $cm->modname, 'intro', $cm->id, $options);
307
                        }
308
 
309
                        //url of the module
310
                        $url = $cm->url;
311
                        if ($url) { //labels don't have url
312
                            $module['url'] = $url->out(false);
313
                        }
314
 
315
                        $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
316
                                            context_module::instance($cm->id));
317
                        //user that can view hidden module should know about the visibility
318
                        $module['visible'] = $cm->visible;
319
                        $module['visibleoncoursepage'] = $cm->visibleoncoursepage;
320
                        $module['uservisible'] = $cm->uservisible;
321
                        if (!empty($cm->availableinfo)) {
322
                            $module['availabilityinfo'] = \core_availability\info::format_info($cm->availableinfo, $course);
323
                        }
324
 
325
                        // Availability date (also send to user who can see hidden module).
326
                        if ($CFG->enableavailability && ($canviewhidden || $canupdatecourse)) {
327
                            $module['availability'] = $cm->availability;
328
                        }
329
 
330
                        // Return contents only if the user can access to the module.
331
                        if ($cm->uservisible) {
332
                            $baseurl = 'webservice/pluginfile.php';
333
 
334
                            // Call $modulename_export_contents (each module callback take care about checking the capabilities).
335
                            require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
336
                            $getcontentfunction = $cm->modname.'_export_contents';
337
                            if (function_exists($getcontentfunction)) {
338
                                $contents = $getcontentfunction($cm, $baseurl);
339
                                $module['contentsinfo'] = array(
340
                                    'filescount' => count($contents),
341
                                    'filessize' => 0,
342
                                    'lastmodified' => 0,
343
                                    'mimetypes' => array(),
344
                                );
345
                                foreach ($contents as $content) {
346
                                    // Check repository file (only main file).
347
                                    if (!isset($module['contentsinfo']['repositorytype'])) {
348
                                        $module['contentsinfo']['repositorytype'] =
349
                                            isset($content['repositorytype']) ? $content['repositorytype'] : '';
350
                                    }
351
                                    if (isset($content['filesize'])) {
352
                                        $module['contentsinfo']['filessize'] += $content['filesize'];
353
                                    }
354
                                    if (isset($content['timemodified']) &&
355
                                            ($content['timemodified'] > $module['contentsinfo']['lastmodified'])) {
356
 
357
                                        $module['contentsinfo']['lastmodified'] = $content['timemodified'];
358
                                    }
359
                                    if (isset($content['mimetype'])) {
360
                                        $module['contentsinfo']['mimetypes'][$content['mimetype']] = $content['mimetype'];
361
                                    }
362
                                }
363
 
364
                                if (empty($filters['excludecontents']) and !empty($contents)) {
365
                                    $module['contents'] = $contents;
366
                                } else {
367
                                    $module['contents'] = array();
368
                                }
369
                            }
370
                        }
371
 
372
                        // Assign result to $sectioncontents, there is an exception,
373
                        // stealth activities in non-visible sections for students go to a special section.
374
                        if (!empty($filters['includestealthmodules']) && !$section->uservisible && $cm->is_stealth()) {
375
                            $stealthmodules[] = $module;
376
                        } else {
377
                            $sectioncontents[] = $module;
378
                        }
379
 
380
                        // If we just did a filtering, break the loop.
381
                        if ($modfound) {
382
                            break;
383
                        }
384
 
385
                    }
386
                }
387
                $sectionvalues['modules'] = $sectioncontents;
388
 
389
                // assign result to $coursecontents
390
                $coursecontents[$key] = $sectionvalues;
391
 
392
                // Break the loop if we are filtering.
393
                if ($sectionfound) {
394
                    break;
395
                }
396
            }
397
 
398
            // Now that we have iterated over all the sections and activities, check the visibility.
399
            // We didn't this before to be able to retrieve stealth activities.
400
            foreach ($coursecontents as $sectionnumber => $sectioncontents) {
401
                $section = $sections[$sectionnumber];
402
 
403
                if (!$courseformat->is_section_visible($section)) {
404
                    unset($coursecontents[$sectionnumber]);
405
                    continue;
406
                }
407
 
408
                // Remove section and modules information if the section is not visible for the user.
409
                if (!$section->uservisible) {
410
                    $coursecontents[$sectionnumber]['modules'] = array();
411
                    // Remove summary information if the section is completely hidden only,
412
                    // even if the section is not user visible, the summary is always displayed among the availability information.
413
                    if (!$section->visible) {
414
                        $coursecontents[$sectionnumber]['summary'] = '';
415
                    }
416
                }
417
            }
418
 
419
            // Include stealth modules in special section (without any info).
420
            if (!empty($stealthmodules)) {
421
                $coursecontents[] = array(
422
                    'id' => -1,
423
                    'name' => '',
424
                    'summary' => '',
425
                    'summaryformat' => FORMAT_MOODLE,
426
                    'modules' => $stealthmodules
427
                );
428
            }
429
 
430
        }
431
        return $coursecontents;
432
    }
433
 
434
    /**
435
     * Returns description of method result value
436
     *
437
     * @return \core_external\external_description
438
     * @since Moodle 2.2
439
     */
440
    public static function get_course_contents_returns() {
441
        $completiondefinition = \core_completion\external\completion_info_exporter::get_read_structure(VALUE_DEFAULT, []);
442
 
443
        return new external_multiple_structure(
444
            new external_single_structure(
445
                array(
446
                    'id' => new external_value(PARAM_INT, 'Section ID'),
447
                    'name' => new external_value(PARAM_RAW, 'Section name'),
448
                    'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
449
                    'summary' => new external_value(PARAM_RAW, 'Section description'),
450
                    'summaryformat' => new external_format_value('summary'),
451
                    'section' => new external_value(PARAM_INT, 'Section number inside the course', VALUE_OPTIONAL),
452
                    'hiddenbynumsections' => new external_value(PARAM_INT, 'Whether is a section hidden in the course format',
453
                                                                VALUE_OPTIONAL),
454
                    'uservisible' => new external_value(PARAM_BOOL, 'Is the section visible for the user?', VALUE_OPTIONAL),
455
                    'availabilityinfo' => new external_value(PARAM_RAW, 'Availability information.', VALUE_OPTIONAL),
1441 ariadna 456
                    'component' => new external_value(PARAM_COMPONENT, 'The delegate component of this section if any.',
457
                        VALUE_OPTIONAL),
458
                    'itemid' => new external_value(PARAM_INT,
459
                        'The optional item id delegate component can use to identify its instance.', VALUE_OPTIONAL),
1 efrain 460
                    'modules' => new external_multiple_structure(
461
                            new external_single_structure(
462
                                array(
463
                                    'id' => new external_value(PARAM_INT, 'activity id'),
464
                                    'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
465
                                    'name' => new external_value(PARAM_RAW, 'activity module name'),
466
                                    'instance' => new external_value(PARAM_INT, 'instance id', VALUE_OPTIONAL),
467
                                    'contextid' => new external_value(PARAM_INT, 'Activity context id.', VALUE_OPTIONAL),
468
                                    'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
469
                                    'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
470
                                    'uservisible' => new external_value(PARAM_BOOL, 'Is the module visible for the user?',
471
                                        VALUE_OPTIONAL),
472
                                    'availabilityinfo' => new external_value(PARAM_RAW, 'Availability information.',
473
                                        VALUE_OPTIONAL),
474
                                    'visibleoncoursepage' => new external_value(PARAM_INT, 'is the module visible on course page',
475
                                        VALUE_OPTIONAL),
476
                                    'modicon' => new external_value(PARAM_URL, 'activity icon url'),
477
                                    'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
478
                                    'purpose' => new external_value(PARAM_ALPHA, 'the module purpose'),
479
                                    'branded' => new external_value(PARAM_BOOL, 'Whether the module is branded or not',
480
                                        VALUE_OPTIONAL),
481
                                    'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
482
                                    'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL),
483
                                    'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
484
                                    'onclick' => new external_value(PARAM_RAW, 'Onclick action.', VALUE_OPTIONAL),
485
                                    'afterlink' => new external_value(PARAM_RAW, 'After link info to be displayed.',
486
                                        VALUE_OPTIONAL),
487
                                    'activitybadge' => self::get_activitybadge_structure(),
488
                                    'customdata' => new external_value(PARAM_RAW, 'Custom data (JSON encoded).', VALUE_OPTIONAL),
489
                                    'noviewlink' => new external_value(PARAM_BOOL, 'Whether the module has no view page',
490
                                        VALUE_OPTIONAL),
491
                                    'completion' => new external_value(PARAM_INT, 'Type of completion tracking:
492
 
493
                                    'completiondata' => $completiondefinition,
494
                                    'downloadcontent' => new external_value(PARAM_INT, 'The download content value', VALUE_OPTIONAL),
495
                                    'dates' => new external_multiple_structure(
496
                                        new external_single_structure(
497
                                            array(
498
                                                'label' => new external_value(PARAM_TEXT, 'date label'),
499
                                                'timestamp' => new external_value(PARAM_INT, 'date timestamp'),
500
                                                'relativeto' => new external_value(PARAM_INT, 'relative date timestamp',
501
                                                    VALUE_OPTIONAL),
502
                                                'dataid' => new external_value(PARAM_NOTAGS, 'cm data id', VALUE_OPTIONAL),
503
                                            )
504
                                        ),
505
                                        'Course dates',
506
                                        VALUE_DEFAULT,
507
                                        []
508
                                    ),
509
                                    'groupmode' => new external_value(PARAM_INT, 'Group mode value', VALUE_OPTIONAL),
510
                                    'contents' => new external_multiple_structure(
511
                                          new external_single_structure(
512
                                              array(
513
                                                  // content info
514
                                                  'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
515
                                                  'filename'=> new external_value(PARAM_FILE, 'filename'),
516
                                                  'filepath'=> new external_value(PARAM_PATH, 'filepath'),
517
                                                  'filesize'=> new external_value(PARAM_INT, 'filesize'),
518
                                                  'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
519
                                                  'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
520
                                                  'timecreated' => new external_value(PARAM_INT, 'Time created'),
521
                                                  'timemodified' => new external_value(PARAM_INT, 'Time modified'),
522
                                                  'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
523
                                                  'mimetype' => new external_value(PARAM_RAW, 'File mime type.', VALUE_OPTIONAL),
524
                                                  'isexternalfile' => new external_value(PARAM_BOOL, 'Whether is an external file.',
525
                                                    VALUE_OPTIONAL),
526
                                                  'repositorytype' => new external_value(PARAM_PLUGIN, 'The repository type for external files.',
527
                                                    VALUE_OPTIONAL),
528
 
529
                                                  // copyright related info
530
                                                  'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
531
                                                  'author' => new external_value(PARAM_TEXT, 'Content owner'),
532
                                                  'license' => new external_value(PARAM_TEXT, 'Content license'),
533
                                                  'tags' => new external_multiple_structure(
534
                                                       \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags',
535
                                                            VALUE_OPTIONAL
536
                                                   ),
537
                                              )
538
                                          ), 'Course contents', VALUE_DEFAULT, array()
539
                                      ),
540
                                    'contentsinfo' => new external_single_structure(
541
                                        array(
542
                                            'filescount' => new external_value(PARAM_INT, 'Total number of files.'),
543
                                            'filessize' => new external_value(PARAM_INT, 'Total files size.'),
544
                                            'lastmodified' => new external_value(PARAM_INT, 'Last time files were modified.'),
545
                                            'mimetypes' => new external_multiple_structure(
546
                                                new external_value(PARAM_RAW, 'File mime type.'),
547
                                                'Files mime types.'
548
                                            ),
549
                                            'repositorytype' => new external_value(PARAM_PLUGIN, 'The repository type for
550
                                                the main file.', VALUE_OPTIONAL),
551
                                        ), 'Contents summary information.', VALUE_OPTIONAL
552
                                    ),
553
                                )
554
                            ), 'list of module'
555
                    )
556
                )
557
            )
558
        );
559
    }
560
 
561
    /**
562
     * Returns description of activitybadge data.
563
     *
564
     * @return external_description
565
     */
566
    protected static function get_activitybadge_structure(): external_description {
567
        return new external_single_structure(
568
            [
569
                'badgecontent' => new external_value(
570
                    PARAM_TEXT,
571
                    'The content to be displayed in the activity badge',
572
                    VALUE_OPTIONAL
573
                ),
574
                'badgestyle' => new external_value(
575
                    PARAM_TEXT,
576
                    'The style for the activity badge',
577
                    VALUE_OPTIONAL
578
                ),
579
                'badgeurl' => new external_value(
580
                    PARAM_URL,
581
                    'An optional URL to redirect the user when the activity badge is clicked',
582
                    VALUE_OPTIONAL
583
                ),
584
                'badgeelementid' => new external_value(
585
                    PARAM_ALPHANUMEXT,
586
                    'An optional id in case the module wants to add some code for the activity badge',
587
                    VALUE_OPTIONAL
588
                ),
589
                'badgeextraattributes' => new external_multiple_structure(
590
                    new external_single_structure(
591
                        [
592
                            'name' => new external_value(
593
                                PARAM_TEXT,
594
                                'The attribute name',
595
                                VALUE_OPTIONAL
596
                            ),
597
                            'value' => new external_value(
598
                                PARAM_TEXT,
599
                                'The attribute value',
600
                                VALUE_OPTIONAL
601
                            ),
602
                        ],
603
                        'Each of the attribute names and values',
604
                        VALUE_OPTIONAL
605
                    ),
606
                    'An optional array of extra HTML attributes to add to the badge element',
607
                    VALUE_OPTIONAL
608
                ),
609
            ],
610
            'Activity badge to display near the name',
611
            VALUE_OPTIONAL
612
        );
613
    }
614
 
615
    /**
616
     * Returns description of method parameters
617
     *
618
     * @return external_function_parameters
619
     * @since Moodle 2.3
620
     */
621
    public static function get_courses_parameters() {
622
        return new external_function_parameters(
623
                array('options' => new external_single_structure(
624
                            array('ids' => new external_multiple_structure(
625
                                        new external_value(PARAM_INT, 'Course id')
626
                                        , 'List of course id. If empty return all courses
627
                                            except front page course.',
628
                                        VALUE_OPTIONAL)
629
                            ), 'options - operator OR is used', VALUE_DEFAULT, array())
630
                )
631
        );
632
    }
633
 
634
    /**
635
     * Get courses
636
     *
637
     * @param array $options It contains an array (list of ids)
638
     * @return array
639
     * @since Moodle 2.2
640
     */
641
    public static function get_courses($options = array()) {
642
        global $CFG, $DB;
643
        require_once($CFG->dirroot . "/course/lib.php");
644
 
645
        //validate parameter
646
        $params = self::validate_parameters(self::get_courses_parameters(),
647
                        array('options' => $options));
648
 
649
        //retrieve courses
650
        if (!array_key_exists('ids', $params['options'])
651
                or empty($params['options']['ids'])) {
652
            $courses = $DB->get_records('course');
653
        } else {
654
            $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
655
        }
656
 
657
        //create return value
658
        $coursesinfo = array();
659
        foreach ($courses as $course) {
660
 
661
            // now security checks
662
            $context = context_course::instance($course->id, IGNORE_MISSING);
663
            $courseformatoptions = course_get_format($course)->get_format_options();
664
            try {
665
                self::validate_context($context);
666
            } catch (Exception $e) {
667
                $exceptionparam = new stdClass();
668
                $exceptionparam->message = $e->getMessage();
669
                $exceptionparam->courseid = $course->id;
670
                throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
671
            }
672
            if ($course->id != SITEID) {
673
                require_capability('moodle/course:view', $context);
674
            }
675
 
676
            $courseinfo = array();
677
            $courseinfo['id'] = $course->id;
678
            $courseinfo['fullname'] = \core_external\util::format_string($course->fullname, $context);
679
            $courseinfo['shortname'] = \core_external\util::format_string($course->shortname, $context);
680
            $courseinfo['displayname'] = \core_external\util::format_string(get_course_display_name_for_list($course), $context);
681
            $courseinfo['categoryid'] = $course->category;
682
            list($courseinfo['summary'], $courseinfo['summaryformat']) =
683
                \core_external\util::format_text($course->summary, $course->summaryformat, $context, 'course', 'summary', 0);
684
            $courseinfo['format'] = $course->format;
685
            $courseinfo['startdate'] = $course->startdate;
686
            $courseinfo['enddate'] = $course->enddate;
687
            $courseinfo['showactivitydates'] = $course->showactivitydates;
688
            $courseinfo['showcompletionconditions'] = $course->showcompletionconditions;
689
            if (array_key_exists('numsections', $courseformatoptions)) {
690
                // For backward-compartibility
691
                $courseinfo['numsections'] = $courseformatoptions['numsections'];
692
            }
693
            $courseinfo['pdfexportfont'] = $course->pdfexportfont;
694
 
695
            $handler = core_course\customfield\course_handler::create();
696
            if ($customfields = $handler->export_instance_data($course->id)) {
697
                $courseinfo['customfields'] = [];
698
                foreach ($customfields as $data) {
699
                    $courseinfo['customfields'][] = [
700
                        'type' => $data->get_type(),
701
                        'value' => $data->get_value(),
702
                        'valueraw' => $data->get_data_controller()->get_value(),
703
                        'name' => $data->get_name(),
704
                        'shortname' => $data->get_shortname()
705
                    ];
706
                }
707
            }
708
 
709
            //some field should be returned only if the user has update permission
710
            $courseadmin = has_capability('moodle/course:update', $context);
711
            if ($courseadmin) {
712
                $courseinfo['categorysortorder'] = $course->sortorder;
713
                $courseinfo['idnumber'] = $course->idnumber;
714
                $courseinfo['showgrades'] = $course->showgrades;
715
                $courseinfo['showreports'] = $course->showreports;
716
                $courseinfo['newsitems'] = $course->newsitems;
717
                $courseinfo['visible'] = $course->visible;
718
                $courseinfo['maxbytes'] = $course->maxbytes;
719
                if (array_key_exists('hiddensections', $courseformatoptions)) {
720
                    // For backward-compartibility
721
                    $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
722
                }
723
                // Return numsections for backward-compatibility with clients who expect it.
724
                $courseinfo['numsections'] = course_get_format($course)->get_last_section_number();
725
                $courseinfo['groupmode'] = $course->groupmode;
726
                $courseinfo['groupmodeforce'] = $course->groupmodeforce;
727
                $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
728
                $courseinfo['lang'] = clean_param($course->lang, PARAM_LANG);
729
                $courseinfo['timecreated'] = $course->timecreated;
730
                $courseinfo['timemodified'] = $course->timemodified;
731
                $courseinfo['forcetheme'] = clean_param($course->theme, PARAM_THEME);
732
                $courseinfo['enablecompletion'] = $course->enablecompletion;
733
                $courseinfo['completionnotify'] = $course->completionnotify;
734
                $courseinfo['courseformatoptions'] = array();
735
                foreach ($courseformatoptions as $key => $value) {
736
                    $courseinfo['courseformatoptions'][] = array(
737
                        'name' => $key,
738
                        'value' => $value
739
                    );
740
                }
741
            }
742
 
743
            if ($courseadmin or $course->visible
744
                    or has_capability('moodle/course:viewhiddencourses', $context)) {
745
                $coursesinfo[] = $courseinfo;
746
            }
747
        }
748
 
749
        return $coursesinfo;
750
    }
751
 
752
    /**
753
     * Returns description of method result value
754
     *
755
     * @return \core_external\external_description
756
     * @since Moodle 2.2
757
     */
758
    public static function get_courses_returns() {
759
        return new external_multiple_structure(
760
                new external_single_structure(
761
                        array(
762
                            'id' => new external_value(PARAM_INT, 'course id'),
763
                            'shortname' => new external_value(PARAM_RAW, 'course short name'),
764
                            'categoryid' => new external_value(PARAM_INT, 'category id'),
765
                            'categorysortorder' => new external_value(PARAM_INT,
766
                                    'sort order into the category', VALUE_OPTIONAL),
767
                            'fullname' => new external_value(PARAM_RAW, 'full name'),
768
                            'displayname' => new external_value(PARAM_RAW, 'course display name'),
769
                            'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
770
                            'summary' => new external_value(PARAM_RAW, 'summary'),
771
                            'summaryformat' => new external_format_value('summary'),
772
                            'format' => new external_value(PARAM_PLUGIN,
773
                                    'course format: weeks, topics, social, site,..'),
774
                            'showgrades' => new external_value(PARAM_INT,
775
                                    '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
776
                            'newsitems' => new external_value(PARAM_INT,
777
                                    'number of recent items appearing on the course page', VALUE_OPTIONAL),
778
                            'startdate' => new external_value(PARAM_INT,
779
                                    'timestamp when the course start'),
780
                            'enddate' => new external_value(PARAM_INT,
781
                                    'timestamp when the course end'),
782
                            'numsections' => new external_value(PARAM_INT,
783
                                    '(deprecated, use courseformatoptions) number of weeks/topics',
784
                                    VALUE_OPTIONAL),
785
                            'maxbytes' => new external_value(PARAM_INT,
786
                                    'largest size of file that can be uploaded into the course',
787
                                    VALUE_OPTIONAL),
788
                            'showreports' => new external_value(PARAM_INT,
789
                                    'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
790
                            'visible' => new external_value(PARAM_INT,
791
                                    '1: available to student, 0:not available', VALUE_OPTIONAL),
792
                            'hiddensections' => new external_value(PARAM_INT,
793
                                    '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
794
                                    VALUE_OPTIONAL),
795
                            'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
796
                                    VALUE_OPTIONAL),
797
                            'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
798
                                    VALUE_OPTIONAL),
799
                            'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
800
                                    VALUE_OPTIONAL),
801
                            'timecreated' => new external_value(PARAM_INT,
802
                                    'timestamp when the course have been created', VALUE_OPTIONAL),
803
                            'timemodified' => new external_value(PARAM_INT,
804
                                    'timestamp when the course have been modified', VALUE_OPTIONAL),
805
                            'enablecompletion' => new external_value(PARAM_INT,
806
                                    'Enabled, control via completion and activity settings. Disbaled,
807
                                        not shown in activity settings.',
808
                                    VALUE_OPTIONAL),
809
                            'completionnotify' => new external_value(PARAM_INT,
810
                                    '1: yes 0: no', VALUE_OPTIONAL),
811
                            'lang' => new external_value(PARAM_SAFEDIR,
812
                                    'forced course language', VALUE_OPTIONAL),
813
                            'forcetheme' => new external_value(PARAM_PLUGIN,
814
                                    'name of the force theme', VALUE_OPTIONAL),
815
                            'courseformatoptions' => new external_multiple_structure(
816
                                new external_single_structure(
817
                                    array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
818
                                        'value' => new external_value(PARAM_RAW, 'course format option value')
819
                                )), 'additional options for particular course format', VALUE_OPTIONAL
820
                             ),
821
                            'showactivitydates' => new external_value(PARAM_BOOL, 'Whether the activity dates are shown or not'),
822
                            'showcompletionconditions' => new external_value(PARAM_BOOL,
823
                                'Whether the activity completion conditions are shown or not'),
824
                            'customfields' => new external_multiple_structure(
825
                                new external_single_structure(
826
                                    ['name' => new external_value(PARAM_RAW, 'The name of the custom field'),
827
                                     'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'),
828
                                     'type'  => new external_value(PARAM_COMPONENT,
829
                                         'The type of the custom field - text, checkbox...'),
830
                                     'valueraw' => new external_value(PARAM_RAW, 'The raw value of the custom field'),
831
                                     'value' => new external_value(PARAM_RAW, 'The value of the custom field')]
832
                                ), 'Custom fields and associated values', VALUE_OPTIONAL),
833
                        ), 'course'
834
                )
835
        );
836
    }
837
 
838
    /**
839
     * Return array of all editable course custom fields indexed by their shortname
840
     *
841
     * @param \context $context
842
     * @param int $courseid
843
     * @return \core_customfield\field_controller[]
844
     */
845
    public static function get_editable_customfields(\context $context, int $courseid = 0): array {
846
        $result = [];
847
 
848
        $handler = \core_course\customfield\course_handler::create();
849
        $handler->set_parent_context($context);
850
 
851
        foreach ($handler->get_editable_fields($courseid) as $field) {
852
            $result[$field->get('shortname')] = $field;
853
        }
854
 
855
        return $result;
856
    }
857
 
858
    /**
859
     * Returns description of method parameters
860
     *
861
     * @return external_function_parameters
862
     * @since Moodle 2.2
863
     */
864
    public static function create_courses_parameters() {
865
        $courseconfig = get_config('moodlecourse'); //needed for many default values
866
        return new external_function_parameters(
867
            array(
868
                'courses' => new external_multiple_structure(
869
                    new external_single_structure(
870
                        array(
871
                            'fullname' => new external_value(PARAM_TEXT, 'full name'),
872
                            'shortname' => new external_value(PARAM_TEXT, 'course short name'),
873
                            'categoryid' => new external_value(PARAM_INT, 'category id'),
874
                            'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
875
                            'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
876
                            'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
877
                            'format' => new external_value(PARAM_PLUGIN,
878
                                    'course format: weeks, topics, social, site,..',
879
                                    VALUE_DEFAULT, $courseconfig->format),
880
                            'showgrades' => new external_value(PARAM_INT,
881
                                    '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
882
                                    $courseconfig->showgrades),
883
                            'newsitems' => new external_value(PARAM_INT,
884
                                    'number of recent items appearing on the course page',
885
                                    VALUE_DEFAULT, $courseconfig->newsitems),
886
                            'startdate' => new external_value(PARAM_INT,
887
                                    'timestamp when the course start', VALUE_OPTIONAL),
888
                            'enddate' => new external_value(PARAM_INT,
889
                                    'timestamp when the course end', VALUE_OPTIONAL),
890
                            'numsections' => new external_value(PARAM_INT,
891
                                    '(deprecated, use courseformatoptions) number of weeks/topics',
892
                                    VALUE_OPTIONAL),
893
                            'maxbytes' => new external_value(PARAM_INT,
894
                                    'largest size of file that can be uploaded into the course',
895
                                    VALUE_DEFAULT, $courseconfig->maxbytes),
896
                            'showreports' => new external_value(PARAM_INT,
897
                                    'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
898
                                    $courseconfig->showreports),
1441 ariadna 899
                            'visible' => new external_value(
900
                                PARAM_INT,
901
                                '1: available to student, 0: not available',
902
                                VALUE_DEFAULT,
903
                                $courseconfig->visible,
904
                            ),
1 efrain 905
                            'hiddensections' => new external_value(PARAM_INT,
906
                                    '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
907
                                    VALUE_OPTIONAL),
908
                            'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
909
                                    VALUE_DEFAULT, $courseconfig->groupmode),
910
                            'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
911
                                    VALUE_DEFAULT, $courseconfig->groupmodeforce),
912
                            'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
913
                                    VALUE_DEFAULT, 0),
914
                            'enablecompletion' => new external_value(PARAM_INT,
915
                                    'Enabled, control via completion and activity settings. Disabled,
916
                                        not shown in activity settings.',
917
                                    VALUE_OPTIONAL),
918
                            'completionnotify' => new external_value(PARAM_INT,
919
                                    '1: yes 0: no', VALUE_OPTIONAL),
920
                            'lang' => new external_value(PARAM_SAFEDIR,
921
                                    'forced course language', VALUE_OPTIONAL),
922
                            'forcetheme' => new external_value(PARAM_PLUGIN,
923
                                    'name of the force theme', VALUE_OPTIONAL),
924
                            'courseformatoptions' => new external_multiple_structure(
925
                                new external_single_structure(
926
                                    array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
927
                                        'value' => new external_value(PARAM_RAW, 'course format option value')
928
                                )),
929
                                    'additional options for particular course format', VALUE_OPTIONAL),
930
                            'customfields' => new external_multiple_structure(
931
                                new external_single_structure(
932
                                    array(
933
                                        'shortname'  => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'),
934
                                        'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
935
                                )), 'custom fields for the course', VALUE_OPTIONAL
936
                            )
937
                    )), 'courses to create'
938
                )
939
            )
940
        );
941
    }
942
 
943
    /**
944
     * Create  courses
945
     *
946
     * @param array $courses
947
     * @return array courses (id and shortname only)
948
     * @since Moodle 2.2
949
     */
950
    public static function create_courses($courses) {
951
        global $CFG, $DB;
952
        require_once($CFG->dirroot . "/course/lib.php");
953
        require_once($CFG->libdir . '/completionlib.php');
954
 
955
        $params = self::validate_parameters(self::create_courses_parameters(),
956
                        array('courses' => $courses));
957
 
1441 ariadna 958
        $courseconfig = get_config('moodlecourse');
959
 
1 efrain 960
        $availablethemes = core_component::get_plugin_list('theme');
961
        $availablelangs = get_string_manager()->get_list_of_translations();
962
 
963
        $transaction = $DB->start_delegated_transaction();
964
 
965
        foreach ($params['courses'] as $course) {
966
 
967
            // Ensure the current user is allowed to run this function
968
            $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
969
            try {
970
                self::validate_context($context);
971
            } catch (Exception $e) {
972
                $exceptionparam = new stdClass();
973
                $exceptionparam->message = $e->getMessage();
974
                $exceptionparam->catid = $course['categoryid'];
975
                throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
976
            }
977
            require_capability('moodle/course:create', $context);
978
 
979
            // Fullname and short name are required to be non-empty.
980
            if (trim($course['fullname']) === '') {
981
                throw new moodle_exception('errorinvalidparam', 'webservice', '', 'fullname');
982
            } else if (trim($course['shortname']) === '') {
983
                throw new moodle_exception('errorinvalidparam', 'webservice', '', 'shortname');
984
            }
985
 
1441 ariadna 986
            // Make sure start/end date are correctly set.
987
            if (!array_key_exists('startdate', $course)) {
988
                $course['startdate'] = usergetmidnight(time());
989
            }
990
            if (!array_key_exists('enddate', $course) && $courseconfig->courseenddateenabled) {
991
                $course['enddate'] = $course['startdate'] + $courseconfig->courseduration;
992
            }
993
 
1 efrain 994
            // Make sure lang is valid
995
            if (array_key_exists('lang', $course)) {
996
                if (empty($availablelangs[$course['lang']])) {
997
                    throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
998
                }
999
                if (!has_capability('moodle/course:setforcedlanguage', $context)) {
1000
                    unset($course['lang']);
1001
                }
1002
            }
1003
 
1004
            // Make sure theme is valid
1005
            if (array_key_exists('forcetheme', $course)) {
1006
                if (!empty($CFG->allowcoursethemes)) {
1007
                    if (empty($availablethemes[$course['forcetheme']])) {
1008
                        throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
1009
                    } else {
1010
                        $course['theme'] = $course['forcetheme'];
1011
                    }
1012
                }
1013
            }
1014
 
1441 ariadna 1015
            $course['showactivitydates'] = $courseconfig->showactivitydates;
1016
 
1 efrain 1017
            //force visibility if ws user doesn't have the permission to set it
1018
            $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
1019
            if (!has_capability('moodle/course:visibility', $context)) {
1020
                $course['visible'] = $category->visible;
1021
            }
1022
 
1023
            //set default value for completion
1024
            if (completion_info::is_enabled_for_site()) {
1025
                if (!array_key_exists('enablecompletion', $course)) {
1026
                    $course['enablecompletion'] = $courseconfig->enablecompletion;
1027
                }
1028
            } else {
1029
                $course['enablecompletion'] = 0;
1030
            }
1031
 
1032
            $course['category'] = $course['categoryid'];
1033
 
1034
            // Summary format.
1035
            $course['summaryformat'] = util::validate_format($course['summaryformat']);
1036
 
1037
            if (!empty($course['courseformatoptions'])) {
1038
                foreach ($course['courseformatoptions'] as $option) {
1039
                    $course[$option['name']] = $option['value'];
1040
                }
1041
            }
1042
 
1043
            // Custom fields.
1044
            if (!empty($course['customfields'])) {
1045
                $customfields = self::get_editable_customfields($context);
1046
                foreach ($course['customfields'] as $field) {
1047
                    if (array_key_exists($field['shortname'], $customfields)) {
1048
                        // Ensure we're populating the element form fields correctly.
1049
                        $controller = \core_customfield\data_controller::create(0, null, $customfields[$field['shortname']]);
1050
                        $course[$controller->get_form_element_name()] = $field['value'];
1051
                    }
1052
                }
1053
            }
1054
 
1055
            //Note: create_course() core function check shortname, idnumber, category
1056
            $course['id'] = create_course((object) $course)->id;
1057
 
1058
            $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
1059
        }
1060
 
1061
        $transaction->allow_commit();
1062
 
1063
        return $resultcourses;
1064
    }
1065
 
1066
    /**
1067
     * Returns description of method result value
1068
     *
1069
     * @return \core_external\external_description
1070
     * @since Moodle 2.2
1071
     */
1072
    public static function create_courses_returns() {
1073
        return new external_multiple_structure(
1074
            new external_single_structure(
1075
                array(
1076
                    'id'       => new external_value(PARAM_INT, 'course id'),
1077
                    'shortname' => new external_value(PARAM_RAW, 'short name'),
1078
                )
1079
            )
1080
        );
1081
    }
1082
 
1083
    /**
1084
     * Update courses
1085
     *
1086
     * @return external_function_parameters
1087
     * @since Moodle 2.5
1088
     */
1089
    public static function update_courses_parameters() {
1090
        return new external_function_parameters(
1091
            array(
1092
                'courses' => new external_multiple_structure(
1093
                    new external_single_structure(
1094
                        array(
1095
                            'id' => new external_value(PARAM_INT, 'ID of the course'),
1096
                            'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL),
1097
                            'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL),
1098
                            'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL),
1099
                            'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
1100
                            'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
1101
                            'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
1102
                            'format' => new external_value(PARAM_PLUGIN,
1103
                                    'course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
1104
                            'showgrades' => new external_value(PARAM_INT,
1105
                                    '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
1106
                            'newsitems' => new external_value(PARAM_INT,
1107
                                    'number of recent items appearing on the course page', VALUE_OPTIONAL),
1108
                            'startdate' => new external_value(PARAM_INT,
1109
                                    'timestamp when the course start', VALUE_OPTIONAL),
1110
                            'enddate' => new external_value(PARAM_INT,
1111
                                    'timestamp when the course end', VALUE_OPTIONAL),
1112
                            'numsections' => new external_value(PARAM_INT,
1113
                                    '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL),
1114
                            'maxbytes' => new external_value(PARAM_INT,
1115
                                    'largest size of file that can be uploaded into the course', VALUE_OPTIONAL),
1116
                            'showreports' => new external_value(PARAM_INT,
1117
                                    'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
1118
                            'visible' => new external_value(PARAM_INT,
1119
                                    '1: available to student, 0:not available', VALUE_OPTIONAL),
1120
                            'hiddensections' => new external_value(PARAM_INT,
1121
                                    '(deprecated, use courseformatoptions) How the hidden sections in the course are
1122
                                        displayed to students', VALUE_OPTIONAL),
1123
                            'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
1124
                            'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
1125
                            'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
1126
                            'enablecompletion' => new external_value(PARAM_INT,
1127
                                    'Enabled, control via completion and activity settings. Disabled,
1128
                                        not shown in activity settings.', VALUE_OPTIONAL),
1129
                            'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
1130
                            'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL),
1131
                            'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL),
1132
                            'courseformatoptions' => new external_multiple_structure(
1133
                                new external_single_structure(
1134
                                    array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
1135
                                        'value' => new external_value(PARAM_RAW, 'course format option value')
1136
                                )), 'additional options for particular course format', VALUE_OPTIONAL),
1137
                            'customfields' => new external_multiple_structure(
1138
                                new external_single_structure(
1139
                                    [
1140
                                        'shortname'  => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'),
1141
                                        'value' => new external_value(PARAM_RAW, 'The value of the custom field')
1142
                                    ]
1143
                                ), 'Custom fields', VALUE_OPTIONAL),
1144
                        )
1145
                    ), 'courses to update'
1146
                )
1147
            )
1148
        );
1149
    }
1150
 
1151
    /**
1152
     * Update courses
1153
     *
1154
     * @param array $courses
1155
     * @since Moodle 2.5
1156
     */
1157
    public static function update_courses($courses) {
1158
        global $CFG, $DB;
1159
        require_once($CFG->dirroot . "/course/lib.php");
1160
        $warnings = array();
1161
 
1162
        $params = self::validate_parameters(self::update_courses_parameters(),
1163
                        array('courses' => $courses));
1164
 
1165
        $availablethemes = core_component::get_plugin_list('theme');
1166
        $availablelangs = get_string_manager()->get_list_of_translations();
1167
 
1168
        foreach ($params['courses'] as $course) {
1169
            // Catch any exception while updating course and return as warning to user.
1170
            try {
1171
                // Ensure the current user is allowed to run this function.
1172
                $context = context_course::instance($course['id'], MUST_EXIST);
1173
                self::validate_context($context);
1174
 
1175
                $oldcourse = course_get_format($course['id'])->get_course();
1176
 
1177
                require_capability('moodle/course:update', $context);
1178
 
1179
                // Check if user can change category.
1180
                if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) {
1181
                    require_capability('moodle/course:changecategory', $context);
1182
                    $course['category'] = $course['categoryid'];
1183
                }
1184
 
1185
                // Check if the user can change fullname, and the new value is non-empty.
1186
                if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) {
1187
                    require_capability('moodle/course:changefullname', $context);
1188
                    if (trim($course['fullname']) === '') {
1189
                        throw new moodle_exception('errorinvalidparam', 'webservice', '', 'fullname');
1190
                    }
1191
                }
1192
 
1193
                // Check if the user can change shortname, and the new value is non-empty.
1194
                if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
1195
                    require_capability('moodle/course:changeshortname', $context);
1196
                    if (trim($course['shortname']) === '') {
1197
                        throw new moodle_exception('errorinvalidparam', 'webservice', '', 'shortname');
1198
                    }
1199
                }
1200
 
1201
                // Check if the user can change the idnumber.
1202
                if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
1203
                    require_capability('moodle/course:changeidnumber', $context);
1204
                }
1205
 
1206
                // Check if user can change summary.
1207
                if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) {
1208
                    require_capability('moodle/course:changesummary', $context);
1209
                }
1210
 
1211
                // Summary format.
1212
                if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) {
1213
                    require_capability('moodle/course:changesummary', $context);
1214
                    $course['summaryformat'] = util::validate_format($course['summaryformat']);
1215
                }
1216
 
1217
                // Check if user can change visibility.
1218
                if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) {
1219
                    require_capability('moodle/course:visibility', $context);
1220
                }
1221
 
1222
                // Make sure lang is valid.
1223
                if (array_key_exists('lang', $course) && ($oldcourse->lang != $course['lang'])) {
1224
                    require_capability('moodle/course:setforcedlanguage', $context);
1225
                    if (empty($availablelangs[$course['lang']])) {
1226
                        throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
1227
                    }
1228
                }
1229
 
1230
                // Make sure theme is valid.
1231
                if (array_key_exists('forcetheme', $course)) {
1232
                    if (!empty($CFG->allowcoursethemes)) {
1233
                        if (empty($availablethemes[$course['forcetheme']])) {
1234
                            throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
1235
                        } else {
1236
                            $course['theme'] = $course['forcetheme'];
1237
                        }
1238
                    }
1239
                }
1240
 
1241
                // Make sure completion is enabled before setting it.
1242
                if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) {
1243
                    $course['enabledcompletion'] = 0;
1244
                }
1245
 
1246
                // Make sure maxbytes are less then CFG->maxbytes.
1247
                if (array_key_exists('maxbytes', $course)) {
1248
                    // We allow updates back to 0 max bytes, a special value denoting the course uses the site limit.
1249
                    // Otherwise, either use the size specified, or cap at the max size for the course.
1250
                    if ($course['maxbytes'] != 0) {
1251
                        $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
1252
                    }
1253
                }
1254
 
1255
                if (!empty($course['courseformatoptions'])) {
1256
                    foreach ($course['courseformatoptions'] as $option) {
1257
                        if (isset($option['name']) && isset($option['value'])) {
1258
                            $course[$option['name']] = $option['value'];
1259
                        }
1260
                    }
1261
                }
1262
 
1263
                // Custom fields.
1264
                if (isset($course['customfields'])) {
1265
                    $customfields = self::get_editable_customfields($context, $course['id']);
1266
                    foreach ($course['customfields'] as $field) {
1267
                        if (array_key_exists($field['shortname'], $customfields)) {
1268
                            // Ensure we're populating the element form fields correctly.
1269
                            $controller = \core_customfield\data_controller::create(0, null, $customfields[$field['shortname']]);
1270
                            $course[$controller->get_form_element_name()] = $field['value'];
1271
                        }
1272
                    }
1273
                }
1274
 
1275
                // Update course if user has all required capabilities.
1276
                update_course((object) $course);
1277
            } catch (Exception $e) {
1278
                $warning = array();
1279
                $warning['item'] = 'course';
1280
                $warning['itemid'] = $course['id'];
1281
                if ($e instanceof moodle_exception) {
1282
                    $warning['warningcode'] = $e->errorcode;
1283
                } else {
1284
                    $warning['warningcode'] = $e->getCode();
1285
                }
1286
                $warning['message'] = $e->getMessage();
1287
                $warnings[] = $warning;
1288
            }
1289
        }
1290
 
1291
        $result = array();
1292
        $result['warnings'] = $warnings;
1293
        return $result;
1294
    }
1295
 
1296
    /**
1297
     * Returns description of method result value
1298
     *
1299
     * @return \core_external\external_description
1300
     * @since Moodle 2.5
1301
     */
1302
    public static function update_courses_returns() {
1303
        return new external_single_structure(
1304
            array(
1305
                'warnings' => new external_warnings()
1306
            )
1307
        );
1308
    }
1309
 
1310
    /**
1311
     * Returns description of method parameters
1312
     *
1313
     * @return external_function_parameters
1314
     * @since Moodle 2.2
1315
     */
1316
    public static function delete_courses_parameters() {
1317
        return new external_function_parameters(
1318
            array(
1319
                'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
1320
            )
1321
        );
1322
    }
1323
 
1324
    /**
1325
     * Delete courses
1326
     *
1327
     * @param array $courseids A list of course ids
1328
     * @since Moodle 2.2
1329
     */
1330
    public static function delete_courses($courseids) {
1331
        global $CFG, $DB;
1332
        require_once($CFG->dirroot."/course/lib.php");
1333
 
1334
        // Parameter validation.
1335
        $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
1336
 
1337
        $warnings = array();
1338
 
1339
        foreach ($params['courseids'] as $courseid) {
1340
            $course = $DB->get_record('course', array('id' => $courseid));
1341
 
1342
            if ($course === false) {
1343
                $warnings[] = array(
1344
                                'item' => 'course',
1345
                                'itemid' => $courseid,
1346
                                'warningcode' => 'unknowncourseidnumber',
1347
                                'message' => 'Unknown course ID ' . $courseid
1348
                            );
1349
                continue;
1350
            }
1351
 
1352
            // Check if the context is valid.
1353
            $coursecontext = context_course::instance($course->id);
1354
            self::validate_context($coursecontext);
1355
 
1356
            // Check if the current user has permission.
1357
            if (!can_delete_course($courseid)) {
1358
                $warnings[] = array(
1359
                                'item' => 'course',
1360
                                'itemid' => $courseid,
1361
                                'warningcode' => 'cannotdeletecourse',
1362
                                'message' => 'You do not have the permission to delete this course' . $courseid
1363
                            );
1364
                continue;
1365
            }
1366
 
1367
            if (delete_course($course, false) === false) {
1368
                $warnings[] = array(
1369
                                'item' => 'course',
1370
                                'itemid' => $courseid,
1371
                                'warningcode' => 'cannotdeletecategorycourse',
1372
                                'message' => 'Course ' . $courseid . ' failed to be deleted'
1373
                            );
1374
                continue;
1375
            }
1376
        }
1377
 
1378
        fix_course_sortorder();
1379
 
1380
        return array('warnings' => $warnings);
1381
    }
1382
 
1383
    /**
1384
     * Returns description of method result value
1385
     *
1386
     * @return \core_external\external_description
1387
     * @since Moodle 2.2
1388
     */
1389
    public static function delete_courses_returns() {
1390
        return new external_single_structure(
1391
            array(
1392
                'warnings' => new external_warnings()
1393
            )
1394
        );
1395
    }
1396
 
1397
    /**
1398
     * Returns description of method parameters
1399
     *
1400
     * @return external_function_parameters
1401
     * @since Moodle 2.3
1402
     */
1403
    public static function duplicate_course_parameters() {
1404
        return new external_function_parameters(
1405
            array(
1406
                'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
1407
                'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
1408
                'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
1409
                'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
1410
                'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
1411
                'options' => new external_multiple_structure(
1412
                    new external_single_structure(
1413
                        array(
1414
                                'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
1415
                                            "activities" (int) Include course activites (default to 1 that is equal to yes),
1416
                                            "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1417
                                            "filters" (int) Include course filters  (default to 1 that is equal to yes),
1418
                                            "users" (int) Include users (default to 0 that is equal to no),
1419
                                            "enrolments" (int) Include enrolment methods (default to 1 - restore only with users),
1420
                                            "role_assignments" (int) Include role assignments  (default to 0 that is equal to no),
1421
                                            "comments" (int) Include user comments  (default to 0 that is equal to no),
1422
                                            "userscompletion" (int) Include user course completion information  (default to 0 that is equal to no),
1423
                                            "logs" (int) Include course logs  (default to 0 that is equal to no),
1424
                                            "grade_histories" (int) Include histories  (default to 0 that is equal to no)'
1425
                                            ),
1426
                                'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1427
                            )
1428
                        )
1429
                    ), 'Course duplication options', VALUE_DEFAULT, array()
1430
                ),
1431
            )
1432
        );
1433
    }
1434
 
1435
    /**
1436
     * Duplicate a course
1437
     *
1438
     * @param int $courseid
1439
     * @param string $fullname Duplicated course fullname
1440
     * @param string $shortname Duplicated course shortname
1441
     * @param int $categoryid Duplicated course parent category id
1442
     * @param int $visible Duplicated course availability
1443
     * @param array $options List of backup options
1444
     * @return array New course info
1445
     * @since Moodle 2.3
1446
     */
1447
    public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
1448
        global $CFG, $USER, $DB;
1449
        require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1450
        require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1451
 
1452
        // Parameter validation.
1453
        $params = self::validate_parameters(
1454
                self::duplicate_course_parameters(),
1455
                array(
1456
                      'courseid' => $courseid,
1457
                      'fullname' => $fullname,
1458
                      'shortname' => $shortname,
1459
                      'categoryid' => $categoryid,
1460
                      'visible' => $visible,
1461
                      'options' => $options
1462
                )
1463
        );
1464
 
1465
        // Context validation.
1466
 
1467
        if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
1468
            throw new moodle_exception('invalidcourseid', 'error');
1469
        }
1470
 
1471
        // Category where duplicated course is going to be created.
1472
        $categorycontext = context_coursecat::instance($params['categoryid']);
1473
        self::validate_context($categorycontext);
1474
 
1475
        // Course to be duplicated.
1476
        $coursecontext = context_course::instance($course->id);
1477
        self::validate_context($coursecontext);
1478
 
1479
        $backupdefaults = array(
1480
            'activities' => 1,
1481
            'blocks' => 1,
1482
            'filters' => 1,
1483
            'users' => 0,
1484
            'enrolments' => backup::ENROL_WITHUSERS,
1485
            'role_assignments' => 0,
1486
            'comments' => 0,
1487
            'userscompletion' => 0,
1488
            'logs' => 0,
1489
            'grade_histories' => 0
1490
        );
1491
 
1492
        $backupsettings = array();
1493
        // Check for backup and restore options.
1494
        if (!empty($params['options'])) {
1495
            foreach ($params['options'] as $option) {
1496
 
1497
                // Strict check for a correct value (allways 1 or 0, true or false).
1498
                $value = clean_param($option['value'], PARAM_INT);
1499
 
1500
                if ($value !== 0 and $value !== 1) {
1501
                    throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1502
                }
1503
 
1504
                if (!isset($backupdefaults[$option['name']])) {
1505
                    throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1506
                }
1507
 
1508
                $backupsettings[$option['name']] = $value;
1509
            }
1510
        }
1511
 
1512
        // Capability checking.
1513
 
1514
        // The backup controller check for this currently, this may be redundant.
1515
        require_capability('moodle/course:create', $categorycontext);
1516
        require_capability('moodle/restore:restorecourse', $categorycontext);
1517
        require_capability('moodle/backup:backupcourse', $coursecontext);
1518
 
1519
        if (!empty($backupsettings['users'])) {
1520
            require_capability('moodle/backup:userinfo', $coursecontext);
1521
            require_capability('moodle/restore:userinfo', $categorycontext);
1522
        }
1523
 
1524
        // Check if the shortname is used.
1525
        if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
1526
            foreach ($foundcourses as $foundcourse) {
1527
                $foundcoursenames[] = $foundcourse->fullname;
1528
            }
1529
 
1530
            $foundcoursenamestring = implode(',', $foundcoursenames);
1531
            throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1532
        }
1533
 
1534
        // Backup the course.
1535
 
1536
        $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1537
        backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
1538
 
1539
        foreach ($backupsettings as $name => $value) {
1540
            if ($setting = $bc->get_plan()->get_setting($name)) {
1541
                $bc->get_plan()->get_setting($name)->set_value($value);
1542
            }
1543
        }
1544
 
1545
        $backupid       = $bc->get_backupid();
1546
        $backupbasepath = $bc->get_plan()->get_basepath();
1547
 
1548
        $bc->execute_plan();
1549
        $results = $bc->get_results();
1550
        $file = $results['backup_destination'];
1551
 
1552
        $bc->destroy();
1553
 
1554
        // Restore the backup immediately.
1555
 
1556
        // Check if we need to unzip the file because the backup temp dir does not contains backup files.
1557
        if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
1558
            $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath);
1559
        }
1560
 
1561
        // Create new course.
1562
        $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1563
 
1564
        $rc = new restore_controller($backupid, $newcourseid,
1565
                backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
1566
 
1567
        foreach ($backupsettings as $name => $value) {
1568
            $setting = $rc->get_plan()->get_setting($name);
1569
            if ($setting->get_status() == backup_setting::NOT_LOCKED) {
1570
                $setting->set_value($value);
1571
            }
1572
        }
1573
 
1574
        if (!$rc->execute_precheck()) {
1575
            $precheckresults = $rc->get_precheck_results();
1576
            if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1577
                if (empty($CFG->keeptempdirectoriesonbackup)) {
1578
                    fulldelete($backupbasepath);
1579
                }
1580
 
1581
                $errorinfo = '';
1582
 
1583
                foreach ($precheckresults['errors'] as $error) {
1584
                    $errorinfo .= $error;
1585
                }
1586
 
1587
                if (array_key_exists('warnings', $precheckresults)) {
1588
                    foreach ($precheckresults['warnings'] as $warning) {
1589
                        $errorinfo .= $warning;
1590
                    }
1591
                }
1592
 
1593
                throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1594
            }
1595
        }
1596
 
1597
        $rc->execute_plan();
1598
        $rc->destroy();
1599
 
1600
        $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
1601
        $course->fullname = $params['fullname'];
1602
        $course->shortname = $params['shortname'];
1603
        $course->visible = $params['visible'];
1604
 
1605
        // Set shortname and fullname back.
1606
        $DB->update_record('course', $course);
1607
 
1608
        if (empty($CFG->keeptempdirectoriesonbackup)) {
1609
            fulldelete($backupbasepath);
1610
        }
1611
 
1612
        // Delete the course backup file created by this WebService. Originally located in the course backups area.
1613
        $file->delete();
1614
 
1615
        return array('id' => $course->id, 'shortname' => $course->shortname);
1616
    }
1617
 
1618
    /**
1619
     * Returns description of method result value
1620
     *
1621
     * @return \core_external\external_description
1622
     * @since Moodle 2.3
1623
     */
1624
    public static function duplicate_course_returns() {
1625
        return new external_single_structure(
1626
            array(
1627
                'id'       => new external_value(PARAM_INT, 'course id'),
1628
                'shortname' => new external_value(PARAM_RAW, 'short name'),
1629
            )
1630
        );
1631
    }
1632
 
1633
    /**
1634
     * Returns description of method parameters for import_course
1635
     *
1636
     * @return external_function_parameters
1637
     * @since Moodle 2.4
1638
     */
1639
    public static function import_course_parameters() {
1640
        return new external_function_parameters(
1641
            array(
1642
                'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
1643
                'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
1644
                'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
1645
                'options' => new external_multiple_structure(
1646
                    new external_single_structure(
1647
                        array(
1648
                                'name' => new external_value(PARAM_ALPHA, 'The backup option name:
1649
                                            "activities" (int) Include course activites (default to 1 that is equal to yes),
1650
                                            "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1651
                                            "filters" (int) Include course filters  (default to 1 that is equal to yes)'
1652
                                            ),
1653
                                'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1654
                            )
1655
                        )
1656
                    ), 'Course import options', VALUE_DEFAULT, array()
1657
                ),
1658
            )
1659
        );
1660
    }
1661
 
1662
    /**
1663
     * Imports a course
1664
     *
1665
     * @param int $importfrom The id of the course we are importing from
1666
     * @param int $importto The id of the course we are importing to
1667
     * @param bool $deletecontent Whether to delete the course we are importing to content
1668
     * @param array $options List of backup options
1669
     * @return null
1670
     * @since Moodle 2.4
1671
     */
1672
    public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
1673
        global $CFG, $USER, $DB;
1674
        require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1675
        require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1676
 
1677
        // Parameter validation.
1678
        $params = self::validate_parameters(
1679
            self::import_course_parameters(),
1680
            array(
1681
                'importfrom' => $importfrom,
1682
                'importto' => $importto,
1683
                'deletecontent' => $deletecontent,
1684
                'options' => $options
1685
            )
1686
        );
1687
 
1688
        if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1689
            throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
1690
        }
1691
 
1692
        // Context validation.
1693
 
1694
        if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
1695
            throw new moodle_exception('invalidcourseid', 'error');
1696
        }
1697
 
1698
        if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
1699
            throw new moodle_exception('invalidcourseid', 'error');
1700
        }
1701
 
1702
        $importfromcontext = context_course::instance($importfrom->id);
1703
        self::validate_context($importfromcontext);
1704
 
1705
        $importtocontext = context_course::instance($importto->id);
1706
        self::validate_context($importtocontext);
1707
 
1708
        $backupdefaults = array(
1709
            'activities' => 1,
1710
            'blocks' => 1,
1711
            'filters' => 1
1712
        );
1713
 
1714
        $backupsettings = array();
1715
 
1716
        // Check for backup and restore options.
1717
        if (!empty($params['options'])) {
1718
            foreach ($params['options'] as $option) {
1719
 
1720
                // Strict check for a correct value (allways 1 or 0, true or false).
1721
                $value = clean_param($option['value'], PARAM_INT);
1722
 
1723
                if ($value !== 0 and $value !== 1) {
1724
                    throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1725
                }
1726
 
1727
                if (!isset($backupdefaults[$option['name']])) {
1728
                    throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1729
                }
1730
 
1731
                $backupsettings[$option['name']] = $value;
1732
            }
1733
        }
1734
 
1735
        // Capability checking.
1736
 
1737
        require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1738
        require_capability('moodle/restore:restoretargetimport', $importtocontext);
1739
 
1740
        $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1741
                backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1742
 
1743
        foreach ($backupsettings as $name => $value) {
1744
            $bc->get_plan()->get_setting($name)->set_value($value);
1745
        }
1746
 
1747
        $backupid       = $bc->get_backupid();
1748
        $backupbasepath = $bc->get_plan()->get_basepath();
1749
 
1750
        $bc->execute_plan();
1751
        $bc->destroy();
1752
 
1753
        // Restore the backup immediately.
1754
 
1755
        // Check if we must delete the contents of the destination course.
1756
        if ($params['deletecontent']) {
1757
            $restoretarget = backup::TARGET_EXISTING_DELETING;
1758
        } else {
1759
            $restoretarget = backup::TARGET_EXISTING_ADDING;
1760
        }
1761
 
1762
        $rc = new restore_controller($backupid, $importto->id,
1763
                backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1764
 
1765
        foreach ($backupsettings as $name => $value) {
1766
            $rc->get_plan()->get_setting($name)->set_value($value);
1767
        }
1768
 
1769
        if (!$rc->execute_precheck()) {
1770
            $precheckresults = $rc->get_precheck_results();
1771
            if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1772
                if (empty($CFG->keeptempdirectoriesonbackup)) {
1773
                    fulldelete($backupbasepath);
1774
                }
1775
 
1776
                $errorinfo = '';
1777
 
1778
                foreach ($precheckresults['errors'] as $error) {
1779
                    $errorinfo .= $error;
1780
                }
1781
 
1782
                if (array_key_exists('warnings', $precheckresults)) {
1783
                    foreach ($precheckresults['warnings'] as $warning) {
1784
                        $errorinfo .= $warning;
1785
                    }
1786
                }
1787
 
1788
                throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1789
            }
1790
        } else {
1791
            if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1792
                restore_dbops::delete_course_content($importto->id);
1793
            }
1794
        }
1795
 
1796
        $rc->execute_plan();
1797
        $rc->destroy();
1798
 
1799
        if (empty($CFG->keeptempdirectoriesonbackup)) {
1800
            fulldelete($backupbasepath);
1801
        }
1802
 
1803
        return null;
1804
    }
1805
 
1806
    /**
1807
     * Returns description of method result value
1808
     *
1809
     * @return \core_external\external_description
1810
     * @since Moodle 2.4
1811
     */
1812
    public static function import_course_returns() {
1813
        return null;
1814
    }
1815
 
1816
    /**
1817
     * Returns description of method parameters
1818
     *
1819
     * @return external_function_parameters
1820
     * @since Moodle 2.3
1821
     */
1822
    public static function get_categories_parameters() {
1823
        return new external_function_parameters(
1824
            array(
1825
                'criteria' => new external_multiple_structure(
1826
                    new external_single_structure(
1827
                        array(
1828
                            'key' => new external_value(PARAM_ALPHA,
1829
                                         'The category column to search, expected keys (value format) are:'.
1830
                                         '"id" (int) the category id,'.
1831
                                         '"ids" (string) category ids separated by commas,'.
1832
                                         '"name" (string) the category name,'.
1833
                                         '"parent" (int) the parent category id,'.
1834
                                         '"idnumber" (string) category idnumber'.
1835
                                         ' - user must have \'moodle/category:manage\' to search on idnumber,'.
1836
                                         '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1837
                                             then the function return all categories that the user can see.'.
1838
                                         ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1839
                                         '"theme" (string) only return the categories having this theme'.
1840
                                         ' - user must have \'moodle/category:manage\' to search on theme'),
1841
                            'value' => new external_value(PARAM_RAW, 'the value to match')
1842
                        )
1843
                    ), 'criteria', VALUE_DEFAULT, array()
1844
                ),
1845
                'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1846
                                          (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1847
            )
1848
        );
1849
    }
1850
 
1851
    /**
1852
     * Get categories
1853
     *
1854
     * @param array $criteria Criteria to match the results
1855
     * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1856
     * @return array list of categories
1857
     * @since Moodle 2.3
1858
     */
1859
    public static function get_categories($criteria = array(), $addsubcategories = true) {
1860
        global $CFG, $DB;
1861
        require_once($CFG->dirroot . "/course/lib.php");
1862
 
1863
        // Validate parameters.
1864
        $params = self::validate_parameters(self::get_categories_parameters(),
1865
                array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1866
 
1867
        // Retrieve the categories.
1868
        $categories = array();
1869
        if (!empty($params['criteria'])) {
1870
 
1871
            $conditions = array();
1872
            $wheres = array();
1873
            foreach ($params['criteria'] as $crit) {
1874
                $key = trim($crit['key']);
1875
 
1876
                // Trying to avoid duplicate keys.
1877
                if (!isset($conditions[$key])) {
1878
 
1879
                    $context = context_system::instance();
1880
                    $value = null;
1881
                    switch ($key) {
1882
                        case 'id':
1883
                            $value = clean_param($crit['value'], PARAM_INT);
1884
                            $conditions[$key] = $value;
1885
                            $wheres[] = $key . " = :" . $key;
1886
                            break;
1887
 
1888
                        case 'ids':
1889
                            $value = clean_param($crit['value'], PARAM_SEQUENCE);
1890
                            $ids = explode(',', $value);
1891
                            list($sqlids, $paramids) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
1892
                            $conditions = array_merge($conditions, $paramids);
1893
                            $wheres[] = 'id ' . $sqlids;
1894
                            break;
1895
 
1896
                        case 'idnumber':
1897
                            if (has_capability('moodle/category:manage', $context)) {
1898
                                $value = clean_param($crit['value'], PARAM_RAW);
1899
                                $conditions[$key] = $value;
1900
                                $wheres[] = $key . " = :" . $key;
1901
                            } else {
1902
                                // We must throw an exception.
1903
                                // Otherwise the dev client would think no idnumber exists.
1904
                                throw new moodle_exception('criteriaerror',
1905
                                        'webservice', '', null,
1906
                                        'You don\'t have the permissions to search on the "idnumber" field.');
1907
                            }
1908
                            break;
1909
 
1910
                        case 'name':
1911
                            $value = clean_param($crit['value'], PARAM_TEXT);
1912
                            $conditions[$key] = $value;
1913
                            $wheres[] = $key . " = :" . $key;
1914
                            break;
1915
 
1916
                        case 'parent':
1917
                            $value = clean_param($crit['value'], PARAM_INT);
1918
                            $conditions[$key] = $value;
1919
                            $wheres[] = $key . " = :" . $key;
1920
                            break;
1921
 
1922
                        case 'visible':
1923
                            if (has_capability('moodle/category:viewhiddencategories', $context)) {
1924
                                $value = clean_param($crit['value'], PARAM_INT);
1925
                                $conditions[$key] = $value;
1926
                                $wheres[] = $key . " = :" . $key;
1927
                            } else {
1928
                                throw new moodle_exception('criteriaerror',
1929
                                        'webservice', '', null,
1930
                                        'You don\'t have the permissions to search on the "visible" field.');
1931
                            }
1932
                            break;
1933
 
1934
                        case 'theme':
1935
                            if (has_capability('moodle/category:manage', $context)) {
1936
                                $value = clean_param($crit['value'], PARAM_THEME);
1937
                                $conditions[$key] = $value;
1938
                                $wheres[] = $key . " = :" . $key;
1939
                            } else {
1940
                                throw new moodle_exception('criteriaerror',
1941
                                        'webservice', '', null,
1942
                                        'You don\'t have the permissions to search on the "theme" field.');
1943
                            }
1944
                            break;
1945
 
1946
                        default:
1947
                            throw new moodle_exception('criteriaerror',
1948
                                    'webservice', '', null,
1949
                                    'You can not search on this criteria: ' . $key);
1950
                    }
1951
                }
1952
            }
1953
 
1954
            if (!empty($wheres)) {
1955
                $wheres = implode(" AND ", $wheres);
1956
 
1957
                $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1958
 
1959
                // Retrieve its sub subcategories (all levels).
1960
                if ($categories and !empty($params['addsubcategories'])) {
1961
                    $newcategories = array();
1962
 
1963
                    // Check if we required visible/theme checks.
1964
                    $additionalselect = '';
1965
                    $additionalparams = array();
1966
                    if (isset($conditions['visible'])) {
1967
                        $additionalselect .= ' AND visible = :visible';
1968
                        $additionalparams['visible'] = $conditions['visible'];
1969
                    }
1970
                    if (isset($conditions['theme'])) {
1971
                        $additionalselect .= ' AND theme= :theme';
1972
                        $additionalparams['theme'] = $conditions['theme'];
1973
                    }
1974
 
1975
                    foreach ($categories as $category) {
1976
                        $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1977
                        $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1978
                        $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
1979
                        $newcategories = $newcategories + $subcategories;   // Both arrays have integer as keys.
1980
                    }
1981
                    $categories = $categories + $newcategories;
1982
                }
1983
            }
1984
 
1985
        } else {
1986
            // Retrieve all categories in the database.
1987
            $categories = $DB->get_records('course_categories');
1988
        }
1989
 
1990
        // The not returned categories. key => category id, value => reason of exclusion.
1991
        $excludedcats = array();
1992
 
1993
        // The returned categories.
1994
        $categoriesinfo = array();
1995
 
1996
        // We need to sort the categories by path.
1997
        // The parent cats need to be checked by the algo first.
1998
        usort($categories, "core_course_external::compare_categories_by_path");
1999
 
2000
        foreach ($categories as $category) {
2001
 
2002
            // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
2003
            $parents = explode('/', $category->path);
2004
            unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
2005
            foreach ($parents as $parentid) {
2006
                // Note: when the parent exclusion was due to the context,
2007
                // the sub category could still be returned.
2008
                if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
2009
                    $excludedcats[$category->id] = 'parent';
2010
                }
2011
            }
2012
 
2013
            // Check the user can use the category context.
2014
            $context = context_coursecat::instance($category->id);
2015
            try {
2016
                self::validate_context($context);
2017
            } catch (Exception $e) {
2018
                $excludedcats[$category->id] = 'context';
2019
 
2020
                // If it was the requested category then throw an exception.
2021
                if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
2022
                    $exceptionparam = new stdClass();
2023
                    $exceptionparam->message = $e->getMessage();
2024
                    $exceptionparam->catid = $category->id;
2025
                    throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
2026
                }
2027
            }
2028
 
2029
            // Return the category information.
2030
            if (!isset($excludedcats[$category->id])) {
2031
 
2032
                // Final check to see if the category is visible to the user.
2033
                if (core_course_category::can_view_category($category)) {
2034
 
2035
                    $categoryinfo = array();
2036
                    $categoryinfo['id'] = $category->id;
2037
                    $categoryinfo['name'] = \core_external\util::format_string($category->name, $context);
2038
                    list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
2039
                        \core_external\util::format_text($category->description, $category->descriptionformat,
2040
                                $context, 'coursecat', 'description', null);
2041
                    $categoryinfo['parent'] = $category->parent;
2042
                    $categoryinfo['sortorder'] = $category->sortorder;
2043
                    $categoryinfo['coursecount'] = $category->coursecount;
2044
                    $categoryinfo['depth'] = $category->depth;
2045
                    $categoryinfo['path'] = $category->path;
2046
 
2047
                    // Some fields only returned for admin.
2048
                    if (has_capability('moodle/category:manage', $context)) {
2049
                        $categoryinfo['idnumber'] = $category->idnumber;
2050
                        $categoryinfo['visible'] = $category->visible;
2051
                        $categoryinfo['visibleold'] = $category->visibleold;
2052
                        $categoryinfo['timemodified'] = $category->timemodified;
2053
                        $categoryinfo['theme'] = clean_param($category->theme, PARAM_THEME);
2054
                    }
2055
 
2056
                    $categoriesinfo[] = $categoryinfo;
2057
                } else {
2058
                    $excludedcats[$category->id] = 'visibility';
2059
                }
2060
            }
2061
        }
2062
 
2063
        // Sorting the resulting array so it looks a bit better for the client developer.
2064
        usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
2065
 
2066
        return $categoriesinfo;
2067
    }
2068
 
2069
    /**
2070
     * Sort categories array by path
2071
     * private function: only used by get_categories
2072
     *
2073
     * @param stdClass $category1
2074
     * @param stdClass $category2
2075
     * @return int result of strcmp
2076
     * @since Moodle 2.3
2077
     */
2078
    private static function compare_categories_by_path($category1, $category2) {
2079
        return strcmp($category1->path, $category2->path);
2080
    }
2081
 
2082
    /**
2083
     * Sort categories array by sortorder
2084
     * private function: only used by get_categories
2085
     *
2086
     * @param array $category1
2087
     * @param array $category2
2088
     * @return int result of strcmp
2089
     * @since Moodle 2.3
2090
     */
2091
    private static function compare_categories_by_sortorder($category1, $category2) {
2092
        return strcmp($category1['sortorder'], $category2['sortorder']);
2093
    }
2094
 
2095
    /**
2096
     * Returns description of method result value
2097
     *
2098
     * @return \core_external\external_description
2099
     * @since Moodle 2.3
2100
     */
2101
    public static function get_categories_returns() {
2102
        return new external_multiple_structure(
2103
            new external_single_structure(
2104
                array(
2105
                    'id' => new external_value(PARAM_INT, 'category id'),
2106
                    'name' => new external_value(PARAM_RAW, 'category name'),
2107
                    'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
2108
                    'description' => new external_value(PARAM_RAW, 'category description'),
2109
                    'descriptionformat' => new external_format_value('description'),
2110
                    'parent' => new external_value(PARAM_INT, 'parent category id'),
2111
                    'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
2112
                    'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
2113
                    'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
2114
                    'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
2115
                    'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
2116
                    'depth' => new external_value(PARAM_INT, 'category depth'),
2117
                    'path' => new external_value(PARAM_TEXT, 'category path'),
2118
                    'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
2119
                ), 'List of categories'
2120
            )
2121
        );
2122
    }
2123
 
2124
    /**
2125
     * Returns description of method parameters
2126
     *
2127
     * @return external_function_parameters
2128
     * @since Moodle 2.3
2129
     */
2130
    public static function create_categories_parameters() {
2131
        return new external_function_parameters(
2132
            array(
2133
                'categories' => new external_multiple_structure(
2134
                        new external_single_structure(
2135
                            array(
2136
                                'name' => new external_value(PARAM_TEXT, 'new category name'),
2137
                                'parent' => new external_value(PARAM_INT,
2138
                                        'the parent category id inside which the new category will be created
2139
                                         - set to 0 for a root category',
2140
                                        VALUE_DEFAULT, 0),
2141
                                'idnumber' => new external_value(PARAM_RAW,
2142
                                        'the new category idnumber', VALUE_OPTIONAL),
2143
                                'description' => new external_value(PARAM_RAW,
2144
                                        'the new category description', VALUE_OPTIONAL),
2145
                                'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
2146
                                'theme' => new external_value(PARAM_THEME,
2147
                                        'the new category theme. This option must be enabled on moodle',
2148
                                        VALUE_OPTIONAL),
2149
                        )
2150
                    )
2151
                )
2152
            )
2153
        );
2154
    }
2155
 
2156
    /**
2157
     * Create categories
2158
     *
2159
     * @param array $categories - see create_categories_parameters() for the array structure
2160
     * @return array - see create_categories_returns() for the array structure
2161
     * @since Moodle 2.3
2162
     */
2163
    public static function create_categories($categories) {
2164
        global $DB;
2165
 
2166
        $params = self::validate_parameters(self::create_categories_parameters(),
2167
                        array('categories' => $categories));
2168
 
2169
        $transaction = $DB->start_delegated_transaction();
2170
 
2171
        $createdcategories = array();
2172
        foreach ($params['categories'] as $category) {
2173
            if ($category['parent']) {
2174
                if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
2175
                    throw new moodle_exception('unknowcategory');
2176
                }
2177
                $context = context_coursecat::instance($category['parent']);
2178
            } else {
2179
                $context = context_system::instance();
2180
            }
2181
            self::validate_context($context);
2182
            require_capability('moodle/category:manage', $context);
2183
 
2184
            // this will validate format and throw an exception if there are errors
2185
            util::validate_format($category['descriptionformat']);
2186
 
2187
            $newcategory = core_course_category::create($category);
2188
            $context = context_coursecat::instance($newcategory->id);
2189
 
2190
            $createdcategories[] = array(
2191
                'id' => $newcategory->id,
2192
                'name' => \core_external\util::format_string($newcategory->name, $context),
2193
            );
2194
        }
2195
 
2196
        $transaction->allow_commit();
2197
 
2198
        return $createdcategories;
2199
    }
2200
 
2201
    /**
2202
     * Returns description of method parameters
2203
     *
2204
     * @return external_function_parameters
2205
     * @since Moodle 2.3
2206
     */
2207
    public static function create_categories_returns() {
2208
        return new external_multiple_structure(
2209
            new external_single_structure(
2210
                array(
2211
                    'id' => new external_value(PARAM_INT, 'new category id'),
2212
                    'name' => new external_value(PARAM_RAW, 'new category name'),
2213
                )
2214
            )
2215
        );
2216
    }
2217
 
2218
    /**
2219
     * Returns description of method parameters
2220
     *
2221
     * @return external_function_parameters
2222
     * @since Moodle 2.3
2223
     */
2224
    public static function update_categories_parameters() {
2225
        return new external_function_parameters(
2226
            array(
2227
                'categories' => new external_multiple_structure(
2228
                    new external_single_structure(
2229
                        array(
2230
                            'id'       => new external_value(PARAM_INT, 'course id'),
2231
                            'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
2232
                            'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
2233
                            'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
2234
                            'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
2235
                            'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
2236
                            'theme' => new external_value(PARAM_THEME,
2237
                                    'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
2238
                        )
2239
                    )
2240
                )
2241
            )
2242
        );
2243
    }
2244
 
2245
    /**
2246
     * Update categories
2247
     *
2248
     * @param array $categories The list of categories to update
2249
     * @return null
2250
     * @since Moodle 2.3
2251
     */
2252
    public static function update_categories($categories) {
2253
        global $DB;
2254
 
2255
        // Validate parameters.
2256
        $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
2257
 
2258
        $transaction = $DB->start_delegated_transaction();
2259
 
2260
        foreach ($params['categories'] as $cat) {
2261
            $category = core_course_category::get($cat['id']);
2262
 
2263
            $categorycontext = context_coursecat::instance($cat['id']);
2264
            self::validate_context($categorycontext);
2265
            require_capability('moodle/category:manage', $categorycontext);
2266
 
2267
            // If the category parent is being changed, check for capability in the new parent category
2268
            if (isset($cat['parent']) && ($cat['parent'] !== $category->parent)) {
2269
                if ($cat['parent'] == 0) {
2270
                    // Creating a top level category requires capability in the system context
2271
                    $parentcontext = context_system::instance();
2272
                } else {
2273
                    // Category context
2274
                    $parentcontext = context_coursecat::instance($cat['parent']);
2275
                }
2276
                self::validate_context($parentcontext);
2277
                require_capability('moodle/category:manage', $parentcontext);
2278
            }
2279
 
2280
            // this will throw an exception if descriptionformat is not valid
2281
            util::validate_format($cat['descriptionformat']);
2282
 
2283
            $category->update($cat);
2284
        }
2285
 
2286
        $transaction->allow_commit();
2287
    }
2288
 
2289
    /**
2290
     * Returns description of method result value
2291
     *
2292
     * @return \core_external\external_description
2293
     * @since Moodle 2.3
2294
     */
2295
    public static function update_categories_returns() {
2296
        return null;
2297
    }
2298
 
2299
    /**
2300
     * Returns description of method parameters
2301
     *
2302
     * @return external_function_parameters
2303
     * @since Moodle 2.3
2304
     */
2305
    public static function delete_categories_parameters() {
2306
        return new external_function_parameters(
2307
            array(
2308
                'categories' => new external_multiple_structure(
2309
                    new external_single_structure(
2310
                        array(
2311
                            'id' => new external_value(PARAM_INT, 'category id to delete'),
2312
                            'newparent' => new external_value(PARAM_INT,
2313
                                'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
2314
                            'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
2315
                                category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
2316
                        )
2317
                    )
2318
                )
2319
            )
2320
        );
2321
    }
2322
 
2323
    /**
2324
     * Delete categories
2325
     *
2326
     * @param array $categories A list of category ids
2327
     * @return array
2328
     * @since Moodle 2.3
2329
     */
2330
    public static function delete_categories($categories) {
2331
        global $CFG, $DB;
2332
        require_once($CFG->dirroot . "/course/lib.php");
2333
 
2334
        // Validate parameters.
2335
        $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
2336
 
2337
        $transaction = $DB->start_delegated_transaction();
2338
 
2339
        foreach ($params['categories'] as $category) {
2340
            $deletecat = core_course_category::get($category['id'], MUST_EXIST);
2341
            $context = context_coursecat::instance($deletecat->id);
2342
            require_capability('moodle/category:manage', $context);
2343
            self::validate_context($context);
2344
            self::validate_context(get_category_or_system_context($deletecat->parent));
2345
 
2346
            if ($category['recursive']) {
2347
                // If recursive was specified, then we recursively delete the category's contents.
2348
                if ($deletecat->can_delete_full()) {
2349
                    $deletecat->delete_full(false);
2350
                } else {
2351
                    throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
2352
                }
2353
            } else {
2354
                // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
2355
                // If the parent is the root, moving is not supported (because a course must always be inside a category).
2356
                // We must move to an existing category.
2357
                if (!empty($category['newparent'])) {
2358
                    $newparentcat = core_course_category::get($category['newparent']);
2359
                } else {
2360
                    $newparentcat = core_course_category::get($deletecat->parent);
2361
                }
2362
 
2363
                // This operation is not allowed. We must move contents to an existing category.
2364
                if (!$newparentcat->id) {
2365
                    throw new moodle_exception('movecatcontentstoroot');
2366
                }
2367
 
2368
                self::validate_context(context_coursecat::instance($newparentcat->id));
2369
                if ($deletecat->can_move_content_to($newparentcat->id)) {
2370
                    $deletecat->delete_move($newparentcat->id, false);
2371
                } else {
2372
                    throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
2373
                }
2374
            }
2375
        }
2376
 
2377
        $transaction->allow_commit();
2378
    }
2379
 
2380
    /**
2381
     * Returns description of method parameters
2382
     *
2383
     * @return external_function_parameters
2384
     * @since Moodle 2.3
2385
     */
2386
    public static function delete_categories_returns() {
2387
        return null;
2388
    }
2389
 
2390
    /**
2391
     * Describes the parameters for delete_modules.
2392
     *
2393
     * @return external_function_parameters
2394
     * @since Moodle 2.5
2395
     */
2396
    public static function delete_modules_parameters() {
2397
        return new external_function_parameters (
2398
            array(
2399
                'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID',
2400
                        VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'),
2401
            )
2402
        );
2403
    }
2404
 
2405
    /**
2406
     * Deletes a list of provided module instances.
2407
     *
2408
     * @param array $cmids the course module ids
2409
     * @since Moodle 2.5
2410
     */
2411
    public static function delete_modules($cmids) {
2412
        global $CFG, $DB;
2413
 
2414
        // Require course file containing the course delete module function.
2415
        require_once($CFG->dirroot . "/course/lib.php");
2416
 
2417
        // Clean the parameters.
2418
        $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids));
2419
 
2420
        // Keep track of the course ids we have performed a capability check on to avoid repeating.
2421
        $arrcourseschecked = array();
2422
 
2423
        foreach ($params['cmids'] as $cmid) {
2424
            // Get the course module.
2425
            $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST);
2426
 
2427
            // Check if we have not yet confirmed they have permission in this course.
2428
            if (!in_array($cm->course, $arrcourseschecked)) {
2429
                // Ensure the current user has required permission in this course.
2430
                $context = context_course::instance($cm->course);
2431
                self::validate_context($context);
2432
                // Add to the array.
2433
                $arrcourseschecked[] = $cm->course;
2434
            }
2435
 
2436
            // Ensure they can delete this module.
2437
            $modcontext = context_module::instance($cm->id);
2438
            require_capability('moodle/course:manageactivities', $modcontext);
2439
 
2440
            // Delete the module.
2441
            course_delete_module($cm->id);
2442
        }
2443
    }
2444
 
2445
    /**
2446
     * Describes the delete_modules return value.
2447
     *
2448
     * @return external_single_structure
2449
     * @since Moodle 2.5
2450
     */
2451
    public static function delete_modules_returns() {
2452
        return null;
2453
    }
2454
 
2455
    /**
2456
     * Returns description of method parameters
2457
     *
2458
     * @return external_function_parameters
2459
     * @since Moodle 2.9
2460
     */
2461
    public static function view_course_parameters() {
2462
        return new external_function_parameters(
2463
            array(
2464
                'courseid' => new external_value(PARAM_INT, 'id of the course'),
2465
                'sectionnumber' => new external_value(PARAM_INT, 'section number', VALUE_DEFAULT, 0)
2466
            )
2467
        );
2468
    }
2469
 
2470
    /**
2471
     * Trigger the course viewed event.
2472
     *
2473
     * @param int $courseid id of course
2474
     * @param int $sectionnumber sectionnumber (0, 1, 2...)
2475
     * @return array of warnings and status result
2476
     * @since Moodle 2.9
2477
     * @throws moodle_exception
2478
     */
2479
    public static function view_course($courseid, $sectionnumber = 0) {
2480
        global $CFG;
2481
        require_once($CFG->dirroot . "/course/lib.php");
2482
 
2483
        $params = self::validate_parameters(self::view_course_parameters(),
2484
                                            array(
2485
                                                'courseid' => $courseid,
2486
                                                'sectionnumber' => $sectionnumber
2487
                                            ));
2488
 
2489
        $warnings = array();
2490
 
2491
        $course = get_course($params['courseid']);
2492
        $context = context_course::instance($course->id);
2493
        self::validate_context($context);
2494
 
2495
        if (!empty($params['sectionnumber'])) {
2496
 
2497
            // Get section details and check it exists.
2498
            $modinfo = get_fast_modinfo($course);
2499
            $coursesection = $modinfo->get_section_info($params['sectionnumber'], MUST_EXIST);
2500
 
2501
            // Check user is allowed to see it.
2502
            if (!$coursesection->uservisible) {
2503
                require_capability('moodle/course:viewhiddensections', $context);
2504
            }
2505
        }
2506
 
2507
        course_view($context, $params['sectionnumber']);
2508
 
2509
        $result = array();
2510
        $result['status'] = true;
2511
        $result['warnings'] = $warnings;
2512
        return $result;
2513
    }
2514
 
2515
    /**
2516
     * Returns description of method result value
2517
     *
2518
     * @return \core_external\external_description
2519
     * @since Moodle 2.9
2520
     */
2521
    public static function view_course_returns() {
2522
        return new external_single_structure(
2523
            array(
2524
                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2525
                'warnings' => new external_warnings()
2526
            )
2527
        );
2528
    }
2529
 
2530
    /**
2531
     * Returns description of method parameters
2532
     *
2533
     * @return external_function_parameters
2534
     * @since Moodle 3.0
2535
     */
2536
    public static function search_courses_parameters() {
2537
        return new external_function_parameters(
2538
            array(
2539
                'criterianame'  => new external_value(PARAM_ALPHA, 'criteria name
2540
                                                        (search, modulelist (only admins), blocklist (only admins), tagid)'),
2541
                'criteriavalue' => new external_value(PARAM_RAW, 'criteria value'),
2542
                'page'          => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0),
2543
                'perpage'       => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
2544
                'requiredcapabilities' => new external_multiple_structure(
2545
                    new external_value(PARAM_CAPABILITY, 'Capability string used to filter courses by permission'),
2546
                    'Optional list of required capabilities (used to filter the list)', VALUE_DEFAULT, array()
2547
                ),
2548
                'limittoenrolled' => new external_value(PARAM_BOOL, 'limit to enrolled courses', VALUE_DEFAULT, 0),
2549
                'onlywithcompletion' => new external_value(PARAM_BOOL, 'limit to courses where completion is enabled',
2550
                    VALUE_DEFAULT, 0),
2551
            )
2552
        );
2553
    }
2554
 
2555
    /**
2556
     * Return the course information that is public (visible by every one)
2557
     *
2558
     * @param  core_course_list_element $course        course in list object
2559
     * @param  stdClass       $coursecontext course context object
2560
     * @return array the course information
2561
     * @since  Moodle 3.2
2562
     */
2563
    protected static function get_course_public_information(core_course_list_element $course, $coursecontext) {
2564
        global $OUTPUT;
2565
 
2566
        static $categoriescache = array();
2567
 
2568
        // Category information.
2569
        if (!array_key_exists($course->category, $categoriescache)) {
2570
            $categoriescache[$course->category] = core_course_category::get($course->category, IGNORE_MISSING);
2571
        }
2572
        $category = $categoriescache[$course->category];
2573
 
2574
        // Retrieve course overview used files.
2575
        $files = array();
2576
        foreach ($course->get_course_overviewfiles() as $file) {
2577
            $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
2578
                                                                    $file->get_filearea(), null, $file->get_filepath(),
2579
                                                                    $file->get_filename())->out(false);
2580
            $files[] = array(
2581
                'filename' => $file->get_filename(),
2582
                'fileurl' => $fileurl,
2583
                'filesize' => $file->get_filesize(),
2584
                'filepath' => $file->get_filepath(),
2585
                'mimetype' => $file->get_mimetype(),
2586
                'timemodified' => $file->get_timemodified(),
2587
            );
2588
        }
2589
 
2590
        // Retrieve the course contacts,
2591
        // we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services.
2592
        $coursecontacts = array();
2593
        foreach ($course->get_course_contacts() as $contact) {
2594
             $coursecontacts[] = array(
2595
                'id' => $contact['user']->id,
2596
                'fullname' => $contact['username'],
2597
                'roles' => array_map(function($role){
2598
                    return array('id' => $role->id, 'name' => $role->displayname);
2599
                }, $contact['roles']),
2600
                'role' => array('id' => $contact['role']->id, 'name' => $contact['role']->displayname),
2601
                'rolename' => $contact['rolename']
2602
             );
2603
        }
2604
 
2605
        // Allowed enrolment methods (maybe we can self-enrol).
2606
        $enroltypes = array();
2607
        $instances = enrol_get_instances($course->id, true);
2608
        foreach ($instances as $instance) {
2609
            $enroltypes[] = $instance->enrol;
2610
        }
2611
 
2612
        // Format summary.
2613
        list($summary, $summaryformat) =
2614
            \core_external\util::format_text($course->summary, $course->summaryformat, $coursecontext, 'course', 'summary', null);
2615
 
2616
        $categoryname = '';
2617
        if (!empty($category)) {
2618
            $categoryname = \core_external\util::format_string($category->name, $category->get_context());
2619
        }
2620
 
2621
        $displayname = get_course_display_name_for_list($course);
2622
        $coursereturns = array();
2623
        $coursereturns['id']                = $course->id;
2624
        $coursereturns['fullname']          = \core_external\util::format_string($course->fullname, $coursecontext);
2625
        $coursereturns['displayname']       = \core_external\util::format_string($displayname, $coursecontext);
2626
        $coursereturns['shortname']         = \core_external\util::format_string($course->shortname, $coursecontext);
2627
        $coursereturns['categoryid']        = $course->category;
2628
        $coursereturns['categoryname']      = $categoryname;
2629
        $coursereturns['summary']           = $summary;
2630
        $coursereturns['summaryformat']     = $summaryformat;
2631
        $coursereturns['summaryfiles']      = util::get_area_files($coursecontext->id, 'course', 'summary', false, false);
2632
        $coursereturns['overviewfiles']     = $files;
2633
        $coursereturns['contacts']          = $coursecontacts;
2634
        $coursereturns['enrollmentmethods'] = $enroltypes;
2635
        $coursereturns['sortorder']         = $course->sortorder;
2636
        $coursereturns['showactivitydates'] = $course->showactivitydates;
2637
        $coursereturns['showcompletionconditions'] = $course->showcompletionconditions;
2638
 
2639
        $handler = core_course\customfield\course_handler::create();
2640
        if ($customfields = $handler->export_instance_data($course->id)) {
2641
            $coursereturns['customfields'] = [];
2642
            foreach ($customfields as $data) {
2643
                $coursereturns['customfields'][] = [
2644
                    'type' => $data->get_type(),
2645
                    'value' => $data->get_value(),
2646
                    'valueraw' => $data->get_data_controller()->get_value(),
2647
                    'name' => $data->get_name(),
2648
                    'shortname' => $data->get_shortname()
2649
                ];
2650
            }
2651
        }
2652
 
2653
        $courseimage = \core_course\external\course_summary_exporter::get_course_image($course);
2654
        if (!$courseimage) {
2655
            $courseimage = $OUTPUT->get_generated_url_for_course($coursecontext);
2656
        }
2657
        $coursereturns['courseimage'] = $courseimage;
2658
 
2659
        return $coursereturns;
2660
    }
2661
 
2662
    /**
2663
     * Search courses following the specified criteria.
2664
     *
2665
     * @param string $criterianame  Criteria name (search, modulelist (only admins), blocklist (only admins), tagid)
2666
     * @param string $criteriavalue Criteria value
2667
     * @param int $page             Page number (for pagination)
2668
     * @param int $perpage          Items per page
2669
     * @param array $requiredcapabilities Optional list of required capabilities (used to filter the list).
2670
     * @param int $limittoenrolled  Limit to only enrolled courses
2671
     * @param int onlywithcompletion Limit to only courses where completion is enabled
2672
     * @return array of course objects and warnings
2673
     * @since Moodle 3.0
2674
     * @throws moodle_exception
2675
     */
2676
    public static function search_courses($criterianame,
2677
                                          $criteriavalue,
2678
                                          $page=0,
2679
                                          $perpage=0,
2680
                                          $requiredcapabilities=array(),
2681
                                          $limittoenrolled=0,
2682
                                          $onlywithcompletion=0) {
2683
        global $CFG;
2684
 
2685
        $warnings = array();
2686
 
2687
        $parameters = array(
2688
            'criterianame'  => $criterianame,
2689
            'criteriavalue' => $criteriavalue,
2690
            'page'          => $page,
2691
            'perpage'       => $perpage,
2692
            'requiredcapabilities' => $requiredcapabilities,
2693
            'limittoenrolled' => $limittoenrolled,
2694
            'onlywithcompletion' => $onlywithcompletion
2695
        );
2696
        $params = self::validate_parameters(self::search_courses_parameters(), $parameters);
2697
        self::validate_context(context_system::instance());
2698
 
2699
        $allowedcriterianames = array('search', 'modulelist', 'blocklist', 'tagid');
2700
        if (!in_array($params['criterianame'], $allowedcriterianames)) {
2701
            throw new invalid_parameter_exception('Invalid value for criterianame parameter (value: '.$params['criterianame'].'),' .
2702
                'allowed values are: '.implode(',', $allowedcriterianames));
2703
        }
2704
 
2705
        if ($params['criterianame'] == 'modulelist' or $params['criterianame'] == 'blocklist') {
2706
            require_capability('moodle/site:config', context_system::instance());
2707
        }
2708
 
2709
        $paramtype = array(
2710
            'search' => PARAM_RAW,
2711
            'modulelist' => PARAM_PLUGIN,
2712
            'blocklist' => PARAM_INT,
2713
            'tagid' => PARAM_INT
2714
        );
2715
        $params['criteriavalue'] = clean_param($params['criteriavalue'], $paramtype[$params['criterianame']]);
2716
 
2717
        // Prepare the search API options.
2718
        $searchcriteria = array();
2719
        $searchcriteria[$params['criterianame']] = $params['criteriavalue'];
2720
        if ($params['onlywithcompletion']) {
2721
            $searchcriteria['onlywithcompletion'] = true;
2722
        }
2723
        if ($params['limittoenrolled']) {
2724
            $searchcriteria['limittoenrolled'] = true;
2725
        }
2726
 
2727
        $options = array();
2728
        if ($params['perpage'] != 0) {
2729
            $offset = $params['page'] * $params['perpage'];
2730
            $options = array('offset' => $offset, 'limit' => $params['perpage']);
2731
        }
2732
 
2733
        // Search the courses.
2734
        $courses = core_course_category::search_courses($searchcriteria, $options, $params['requiredcapabilities']);
2735
        $totalcount = core_course_category::search_courses_count($searchcriteria, $options, $params['requiredcapabilities']);
2736
 
2737
        $finalcourses = array();
2738
        $categoriescache = array();
2739
 
2740
        foreach ($courses as $course) {
2741
            $coursecontext = context_course::instance($course->id);
2742
 
2743
            $finalcourses[] = self::get_course_public_information($course, $coursecontext);
2744
        }
2745
 
2746
        return array(
2747
            'total' => $totalcount,
2748
            'courses' => $finalcourses,
2749
            'warnings' => $warnings
2750
        );
2751
    }
2752
 
2753
    /**
2754
     * Returns a course structure definition
2755
     *
2756
     * @param  boolean $onlypublicdata set to true, to retrieve only fields viewable by anyone when the course is visible
2757
     * @return external_single_structure the course structure
2758
     * @since  Moodle 3.2
2759
     */
2760
    protected static function get_course_structure($onlypublicdata = true) {
2761
        $coursestructure = array(
2762
            'id' => new external_value(PARAM_INT, 'course id'),
2763
            'fullname' => new external_value(PARAM_RAW, 'course full name'),
2764
            'displayname' => new external_value(PARAM_RAW, 'course display name'),
2765
            'shortname' => new external_value(PARAM_RAW, 'course short name'),
2766
            'courseimage' => new external_value(PARAM_URL, 'Course image', VALUE_OPTIONAL),
2767
            'categoryid' => new external_value(PARAM_INT, 'category id'),
2768
            'categoryname' => new external_value(PARAM_RAW, 'category name'),
2769
            'sortorder' => new external_value(PARAM_INT, 'Sort order in the category', VALUE_OPTIONAL),
2770
            'summary' => new external_value(PARAM_RAW, 'summary'),
2771
            'summaryformat' => new external_format_value('summary'),
2772
            'summaryfiles' => new external_files('summary files in the summary field', VALUE_OPTIONAL),
2773
            'overviewfiles' => new external_files('additional overview files attached to this course'),
2774
            'showactivitydates' => new external_value(PARAM_BOOL, 'Whether the activity dates are shown or not'),
2775
            'showcompletionconditions' => new external_value(PARAM_BOOL,
2776
                'Whether the activity completion conditions are shown or not'),
2777
            'contacts' => new external_multiple_structure(
2778
                new external_single_structure(
2779
                    array(
2780
                        'id' => new external_value(PARAM_INT, 'contact user id'),
2781
                        'fullname'  => new external_value(PARAM_NOTAGS, 'contact user fullname'),
2782
                    )
2783
                ),
2784
                'contact users'
2785
            ),
2786
            'enrollmentmethods' => new external_multiple_structure(
2787
                new external_value(PARAM_PLUGIN, 'enrollment method'),
2788
                'enrollment methods list'
2789
            ),
2790
            'customfields' => new external_multiple_structure(
2791
                new external_single_structure(
2792
                    array(
2793
                        'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
2794
                        'shortname' => new external_value(PARAM_RAW,
2795
                            'The shortname of the custom field - to be able to build the field class in the code'),
2796
                        'type'  => new external_value(PARAM_ALPHANUMEXT,
2797
                            'The type of the custom field - text field, checkbox...'),
2798
                        'valueraw' => new external_value(PARAM_RAW, 'The raw value of the custom field'),
2799
                        'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
2800
                    )
2801
                ), 'Custom fields', VALUE_OPTIONAL),
2802
        );
2803
 
2804
        if (!$onlypublicdata) {
2805
            $extra = array(
2806
                'idnumber' => new external_value(PARAM_RAW, 'Id number', VALUE_OPTIONAL),
2807
                'format' => new external_value(PARAM_PLUGIN, 'Course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
2808
                'showgrades' => new external_value(PARAM_INT, '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
2809
                'newsitems' => new external_value(PARAM_INT, 'Number of recent items appearing on the course page', VALUE_OPTIONAL),
2810
                'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL),
2811
                'enddate' => new external_value(PARAM_INT, 'Timestamp when the course end', VALUE_OPTIONAL),
2812
                'maxbytes' => new external_value(PARAM_INT, 'Largest size of file that can be uploaded into', VALUE_OPTIONAL),
2813
                'showreports' => new external_value(PARAM_INT, 'Are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
2814
                'visible' => new external_value(PARAM_INT, '1: available to student, 0:not available', VALUE_OPTIONAL),
2815
                'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
2816
                'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
2817
                'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
2818
                'enablecompletion' => new external_value(PARAM_INT, 'Completion enabled? 1: yes 0: no', VALUE_OPTIONAL),
2819
                'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
2820
                'lang' => new external_value(PARAM_SAFEDIR, 'Forced course language', VALUE_OPTIONAL),
2821
                'theme' => new external_value(PARAM_PLUGIN, 'Fame of the forced theme', VALUE_OPTIONAL),
2822
                'marker' => new external_value(PARAM_INT, 'Current course marker', VALUE_OPTIONAL),
2823
                'legacyfiles' => new external_value(PARAM_INT, 'If legacy files are enabled', VALUE_OPTIONAL),
2824
                'calendartype' => new external_value(PARAM_PLUGIN, 'Calendar type', VALUE_OPTIONAL),
2825
                'timecreated' => new external_value(PARAM_INT, 'Time when the course was created', VALUE_OPTIONAL),
2826
                'timemodified' => new external_value(PARAM_INT, 'Last time  the course was updated', VALUE_OPTIONAL),
2827
                'requested' => new external_value(PARAM_INT, 'If is a requested course', VALUE_OPTIONAL),
2828
                'cacherev' => new external_value(PARAM_INT, 'Cache revision number', VALUE_OPTIONAL),
2829
                'filters' => new external_multiple_structure(
2830
                    new external_single_structure(
2831
                        array(
2832
                            'filter'  => new external_value(PARAM_PLUGIN, 'Filter plugin name'),
2833
                            'localstate' => new external_value(PARAM_INT, 'Filter state: 1 for on, -1 for off, 0 if inherit'),
2834
                            'inheritedstate' => new external_value(PARAM_INT, '1 or 0 to use when localstate is set to inherit'),
2835
                        )
2836
                    ),
2837
                    'Course filters', VALUE_OPTIONAL
2838
                ),
2839
                'courseformatoptions' => new external_multiple_structure(
2840
                    new external_single_structure(
2841
                        array(
2842
                            'name' => new external_value(PARAM_RAW, 'Course format option name.'),
2843
                            'value' => new external_value(PARAM_RAW, 'Course format option value.'),
2844
                        )
2845
                    ),
2846
                    'Additional options for particular course format.', VALUE_OPTIONAL
2847
                ),
2848
                'communicationroomname' => new external_value(PARAM_TEXT, 'Communication tool room name.', VALUE_OPTIONAL),
2849
                'communicationroomurl' => new external_value(PARAM_RAW, 'Communication tool room URL.', VALUE_OPTIONAL),
2850
            );
2851
            $coursestructure = array_merge($coursestructure, $extra);
2852
        }
2853
        return new external_single_structure($coursestructure);
2854
    }
2855
 
2856
    /**
2857
     * Returns description of method result value
2858
     *
2859
     * @return \core_external\external_description
2860
     * @since Moodle 3.0
2861
     */
2862
    public static function search_courses_returns() {
2863
        return new external_single_structure(
2864
            array(
2865
                'total' => new external_value(PARAM_INT, 'total course count'),
2866
                'courses' => new external_multiple_structure(self::get_course_structure(), 'course'),
2867
                'warnings' => new external_warnings()
2868
            )
2869
        );
2870
    }
2871
 
2872
    /**
2873
     * Returns description of method parameters
2874
     *
2875
     * @return external_function_parameters
2876
     * @since Moodle 3.0
2877
     */
2878
    public static function get_course_module_parameters() {
2879
        return new external_function_parameters(
2880
            array(
2881
                'cmid' => new external_value(PARAM_INT, 'The course module id')
2882
            )
2883
        );
2884
    }
2885
 
2886
    /**
2887
     * Return information about a course module.
2888
     *
2889
     * @param int $cmid the course module id
2890
     * @return array of warnings and the course module
2891
     * @since Moodle 3.0
2892
     * @throws moodle_exception
2893
     */
2894
    public static function get_course_module($cmid) {
2895
        global $CFG, $DB;
2896
 
2897
        $params = self::validate_parameters(self::get_course_module_parameters(), array('cmid' => $cmid));
2898
        $warnings = array();
2899
 
2900
        $cm = get_coursemodule_from_id(null, $params['cmid'], 0, true, MUST_EXIST);
2901
        $context = context_module::instance($cm->id);
2902
        self::validate_context($context);
2903
 
2904
        // If the user has permissions to manage the activity, return all the information.
2905
        if (has_capability('moodle/course:manageactivities', $context)) {
2906
            require_once($CFG->dirroot . '/course/modlib.php');
2907
            require_once($CFG->libdir . '/gradelib.php');
2908
 
2909
            $info = $cm;
2910
            // Get the extra information: grade, advanced grading and outcomes data.
2911
            $course = get_course($cm->course);
2912
            list($newcm, $newcontext, $module, $extrainfo, $cw) = get_moduleinfo_data($cm, $course);
2913
            // Grades.
2914
            $gradeinfo = array('grade', 'gradepass', 'gradecat');
2915
            foreach ($gradeinfo as $gfield) {
2916
                if (isset($extrainfo->{$gfield})) {
2917
                    $info->{$gfield} = $extrainfo->{$gfield};
2918
                }
2919
            }
2920
            if (isset($extrainfo->grade) and $extrainfo->grade < 0) {
2921
                $info->scale = $DB->get_field('scale', 'scale', array('id' => abs($extrainfo->grade)));
2922
            }
2923
            // Advanced grading.
2924
            if (isset($extrainfo->_advancedgradingdata)) {
2925
                $info->advancedgrading = array();
2926
                foreach ($extrainfo as $key => $val) {
2927
                    if (strpos($key, 'advancedgradingmethod_') === 0) {
2928
                        $info->advancedgrading[] = array(
2929
                            'area' => str_replace('advancedgradingmethod_', '', $key),
2930
                            'method' => $val
2931
                        );
2932
                    }
2933
                }
2934
            }
2935
            // Outcomes.
2936
            foreach ($extrainfo as $key => $val) {
2937
                if (strpos($key, 'outcome_') === 0) {
2938
                    if (!isset($info->outcomes)) {
2939
                        $info->outcomes = array();
2940
                    }
2941
                    $id = str_replace('outcome_', '', $key);
2942
                    $outcome = grade_outcome::fetch(array('id' => $id));
2943
                    $scaleitems = $outcome->load_scale();
2944
                    $info->outcomes[] = array(
2945
                        'id' => $id,
2946
                        'name' => \core_external\util::format_string($outcome->get_name(), $context),
2947
                        'scale' => $scaleitems->scale
2948
                    );
2949
                }
2950
            }
2951
        } else {
2952
            // Return information is safe to show to any user.
2953
            $info = new stdClass();
2954
            $info->id = $cm->id;
2955
            $info->course = $cm->course;
2956
            $info->module = $cm->module;
2957
            $info->modname = $cm->modname;
2958
            $info->instance = $cm->instance;
2959
            $info->section = $cm->section;
2960
            $info->sectionnum = $cm->sectionnum;
2961
            $info->groupmode = $cm->groupmode;
2962
            $info->groupingid = $cm->groupingid;
2963
            $info->completion = $cm->completion;
2964
            $info->downloadcontent = $cm->downloadcontent;
2965
        }
2966
        // Format name.
2967
        $info->name = \core_external\util::format_string($cm->name, $context);
2968
        $result = array();
2969
        $result['cm'] = $info;
2970
        $result['warnings'] = $warnings;
2971
        return $result;
2972
    }
2973
 
2974
    /**
2975
     * Returns description of method result value
2976
     *
2977
     * @return \core_external\external_description
2978
     * @since Moodle 3.0
2979
     */
2980
    public static function get_course_module_returns() {
2981
        return new external_single_structure(
2982
            array(
2983
                'cm' => new external_single_structure(
2984
                    array(
2985
                        'id' => new external_value(PARAM_INT, 'The course module id'),
2986
                        'course' => new external_value(PARAM_INT, 'The course id'),
2987
                        'module' => new external_value(PARAM_INT, 'The module type id'),
2988
                        'name' => new external_value(PARAM_RAW, 'The activity name'),
2989
                        'modname' => new external_value(PARAM_COMPONENT, 'The module component name (forum, assign, etc..)'),
2990
                        'instance' => new external_value(PARAM_INT, 'The activity instance id'),
2991
                        'section' => new external_value(PARAM_INT, 'The module section id'),
2992
                        'sectionnum' => new external_value(PARAM_INT, 'The module section number'),
2993
                        'groupmode' => new external_value(PARAM_INT, 'Group mode'),
2994
                        'groupingid' => new external_value(PARAM_INT, 'Grouping id'),
2995
                        'completion' => new external_value(PARAM_INT, 'If completion is enabled'),
2996
                        'idnumber' => new external_value(PARAM_RAW, 'Module id number', VALUE_OPTIONAL),
2997
                        'added' => new external_value(PARAM_INT, 'Time added', VALUE_OPTIONAL),
2998
                        'score' => new external_value(PARAM_INT, 'Score', VALUE_OPTIONAL),
2999
                        'indent' => new external_value(PARAM_INT, 'Indentation', VALUE_OPTIONAL),
3000
                        'visible' => new external_value(PARAM_INT, 'If visible', VALUE_OPTIONAL),
3001
                        'visibleoncoursepage' => new external_value(PARAM_INT, 'If visible on course page', VALUE_OPTIONAL),
3002
                        'visibleold' => new external_value(PARAM_INT, 'Visible old', VALUE_OPTIONAL),
3003
                        'completiongradeitemnumber' => new external_value(PARAM_INT, 'Completion grade item', VALUE_OPTIONAL),
3004
                        'completionpassgrade' => new external_value(PARAM_INT, 'Completion pass grade setting', VALUE_OPTIONAL),
3005
                        'completionview' => new external_value(PARAM_INT, 'Completion view setting', VALUE_OPTIONAL),
3006
                        'completionexpected' => new external_value(PARAM_INT, 'Completion time expected', VALUE_OPTIONAL),
3007
                        'showdescription' => new external_value(PARAM_INT, 'If the description is showed', VALUE_OPTIONAL),
3008
                        'downloadcontent' => new external_value(PARAM_INT, 'The download content value', VALUE_OPTIONAL),
3009
                        'availability' => new external_value(PARAM_RAW, 'Availability settings', VALUE_OPTIONAL),
3010
                        'grade' => new external_value(PARAM_FLOAT, 'Grade (max value or scale id)', VALUE_OPTIONAL),
3011
                        'scale' => new external_value(PARAM_TEXT, 'Scale items (if used)', VALUE_OPTIONAL),
3012
                        'gradepass' => new external_value(PARAM_RAW, 'Grade to pass (float)', VALUE_OPTIONAL),
3013
                        'gradecat' => new external_value(PARAM_INT, 'Grade category', VALUE_OPTIONAL),
3014
                        'advancedgrading' => new external_multiple_structure(
3015
                            new external_single_structure(
3016
                                array(
3017
                                    'area' => new external_value(PARAM_AREA, 'Gradable area name'),
3018
                                    'method'  => new external_value(PARAM_COMPONENT, 'Grading method'),
3019
                                )
3020
                            ),
3021
                            'Advanced grading settings', VALUE_OPTIONAL
3022
                        ),
3023
                        'outcomes' => new external_multiple_structure(
3024
                            new external_single_structure(
3025
                                array(
3026
                                    'id' => new external_value(PARAM_ALPHANUMEXT, 'Outcome id'),
3027
                                    'name'  => new external_value(PARAM_RAW, 'Outcome full name'),
3028
                                    'scale' => new external_value(PARAM_TEXT, 'Scale items')
3029
                                )
3030
                            ),
3031
                            'Outcomes information', VALUE_OPTIONAL
3032
                        ),
3033
                    )
3034
                ),
3035
                'warnings' => new external_warnings()
3036
            )
3037
        );
3038
    }
3039
 
3040
    /**
3041
     * Returns description of method parameters
3042
     *
3043
     * @return external_function_parameters
3044
     * @since Moodle 3.0
3045
     */
3046
    public static function get_course_module_by_instance_parameters() {
3047
        return new external_function_parameters(
3048
            array(
3049
                'module' => new external_value(PARAM_COMPONENT, 'The module name'),
3050
                'instance' => new external_value(PARAM_INT, 'The module instance id')
3051
            )
3052
        );
3053
    }
3054
 
3055
    /**
3056
     * Return information about a course module.
3057
     *
3058
     * @param string $module the module name
3059
     * @param int $instance the activity instance id
3060
     * @return array of warnings and the course module
3061
     * @since Moodle 3.0
3062
     * @throws moodle_exception
3063
     */
3064
    public static function get_course_module_by_instance($module, $instance) {
3065
 
3066
        $params = self::validate_parameters(self::get_course_module_by_instance_parameters(),
3067
                                            array(
3068
                                                'module' => $module,
3069
                                                'instance' => $instance,
3070
                                            ));
3071
 
3072
        $warnings = array();
3073
        $cm = get_coursemodule_from_instance($params['module'], $params['instance'], 0, false, MUST_EXIST);
3074
 
3075
        return self::get_course_module($cm->id);
3076
    }
3077
 
3078
    /**
3079
     * Returns description of method result value
3080
     *
3081
     * @return \core_external\external_description
3082
     * @since Moodle 3.0
3083
     */
3084
    public static function get_course_module_by_instance_returns() {
3085
        return self::get_course_module_returns();
3086
    }
3087
 
3088
    /**
3089
     * Returns description of method parameters
3090
     *
3091
     * @return external_function_parameters
3092
     * @since Moodle 3.2
3093
     */
3094
    public static function get_user_navigation_options_parameters() {
3095
        return new external_function_parameters(
3096
            array(
3097
                'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')),
3098
            )
3099
        );
3100
    }
3101
 
3102
    /**
3103
     * Return a list of navigation options in a set of courses that are avaialable or not for the current user.
3104
     *
3105
     * @param array $courseids a list of course ids
3106
     * @return array of warnings and the options availability
3107
     * @since Moodle 3.2
3108
     * @throws moodle_exception
3109
     */
3110
    public static function get_user_navigation_options($courseids) {
3111
        global $CFG;
3112
        require_once($CFG->dirroot . '/course/lib.php');
3113
 
3114
        // Parameter validation.
3115
        $params = self::validate_parameters(self::get_user_navigation_options_parameters(), array('courseids' => $courseids));
3116
        $courseoptions = array();
3117
 
3118
        list($courses, $warnings) = util::validate_courses($params['courseids'], array(), true);
3119
 
3120
        if (!empty($courses)) {
3121
            foreach ($courses as $course) {
3122
                // Fix the context for the frontpage.
3123
                if ($course->id == SITEID) {
3124
                    $course->context = context_system::instance();
3125
                }
3126
                $navoptions = course_get_user_navigation_options($course->context, $course);
3127
                $options = array();
3128
                foreach ($navoptions as $name => $available) {
3129
                    $options[] = array(
3130
                        'name' => $name,
3131
                        'available' => $available,
3132
                    );
3133
                }
3134
 
3135
                $courseoptions[] = array(
3136
                    'id' => $course->id,
3137
                    'options' => $options
3138
                );
3139
            }
3140
        }
3141
 
3142
        $result = array(
3143
            'courses' => $courseoptions,
3144
            'warnings' => $warnings
3145
        );
3146
        return $result;
3147
    }
3148
 
3149
    /**
3150
     * Returns description of method result value
3151
     *
3152
     * @return \core_external\external_description
3153
     * @since Moodle 3.2
3154
     */
3155
    public static function get_user_navigation_options_returns() {
3156
        return new external_single_structure(
3157
            array(
3158
                'courses' => new external_multiple_structure(
3159
                    new external_single_structure(
3160
                        array(
3161
                            'id' => new external_value(PARAM_INT, 'Course id'),
3162
                            'options' => new external_multiple_structure(
3163
                                new external_single_structure(
3164
                                    array(
3165
                                        'name' => new external_value(PARAM_ALPHANUMEXT, 'Option name'),
3166
                                        'available' => new external_value(PARAM_BOOL, 'Whether the option is available or not'),
3167
                                    )
3168
                                )
3169
                            )
3170
                        )
3171
                    ), 'List of courses'
3172
                ),
3173
                'warnings' => new external_warnings()
3174
            )
3175
        );
3176
    }
3177
 
3178
    /**
3179
     * Returns description of method parameters
3180
     *
3181
     * @return external_function_parameters
3182
     * @since Moodle 3.2
3183
     */
3184
    public static function get_user_administration_options_parameters() {
3185
        return new external_function_parameters(
3186
            array(
3187
                'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')),
3188
            )
3189
        );
3190
    }
3191
 
3192
    /**
3193
     * Return a list of administration options in a set of courses that are available or not for the current user.
3194
     *
3195
     * @param array $courseids a list of course ids
3196
     * @return array of warnings and the options availability
3197
     * @since Moodle 3.2
3198
     * @throws moodle_exception
3199
     */
3200
    public static function get_user_administration_options($courseids) {
3201
        global $CFG;
3202
        require_once($CFG->dirroot . '/course/lib.php');
3203
 
3204
        // Parameter validation.
3205
        $params = self::validate_parameters(self::get_user_administration_options_parameters(), array('courseids' => $courseids));
3206
        $courseoptions = array();
3207
 
3208
        list($courses, $warnings) = util::validate_courses($params['courseids'], array(), true);
3209
 
3210
        if (!empty($courses)) {
3211
            foreach ($courses as $course) {
3212
                $adminoptions = course_get_user_administration_options($course, $course->context);
3213
                $options = array();
3214
                foreach ($adminoptions as $name => $available) {
3215
                    $options[] = array(
3216
                        'name' => $name,
3217
                        'available' => $available,
3218
                    );
3219
                }
3220
 
3221
                $courseoptions[] = array(
3222
                    'id' => $course->id,
3223
                    'options' => $options
3224
                );
3225
            }
3226
        }
3227
 
3228
        $result = array(
3229
            'courses' => $courseoptions,
3230
            'warnings' => $warnings
3231
        );
3232
        return $result;
3233
    }
3234
 
3235
    /**
3236
     * Returns description of method result value
3237
     *
3238
     * @return \core_external\external_description
3239
     * @since Moodle 3.2
3240
     */
3241
    public static function get_user_administration_options_returns() {
3242
        return self::get_user_navigation_options_returns();
3243
    }
3244
 
3245
    /**
3246
     * Returns description of method parameters
3247
     *
3248
     * @return external_function_parameters
3249
     * @since Moodle 3.2
3250
     */
3251
    public static function get_courses_by_field_parameters() {
3252
        return new external_function_parameters(
3253
            array(
3254
                'field' => new external_value(PARAM_ALPHA, 'The field to search can be left empty for all courses or:
3255
                    id: course id
3256
                    ids: comma separated course ids
3257
                    shortname: course short name
3258
                    idnumber: course id number
3259
                    category: category id the course belongs to
1441 ariadna 3260
                    sectionid: section id that belongs to a course
1 efrain 3261
                ', VALUE_DEFAULT, ''),
3262
                'value' => new external_value(PARAM_RAW, 'The value to match', VALUE_DEFAULT, '')
3263
            )
3264
        );
3265
    }
3266
 
3267
    /**
3268
     * Get courses matching a specific field (id/s, shortname, idnumber, category)
3269
     *
3270
     * @param  string $field field name to search, or empty for all courses
3271
     * @param  string $value value to search
3272
     * @return array list of courses and warnings
3273
     * @throws  invalid_parameter_exception
3274
     * @since Moodle 3.2
3275
     */
3276
    public static function get_courses_by_field($field = '', $value = '') {
3277
        global $DB, $CFG;
3278
        require_once($CFG->dirroot . '/course/lib.php');
3279
        require_once($CFG->libdir . '/filterlib.php');
3280
 
3281
        $params = self::validate_parameters(self::get_courses_by_field_parameters(),
3282
            array(
3283
                'field' => $field,
3284
                'value' => $value,
3285
            )
3286
        );
3287
        $warnings = array();
3288
 
3289
        if (empty($params['field'])) {
3290
            $courses = $DB->get_records('course', null, 'id ASC');
3291
        } else {
3292
            switch ($params['field']) {
3293
                case 'id':
3294
                case 'category':
1441 ariadna 3295
                case 'sectionid':
1 efrain 3296
                    $value = clean_param($params['value'], PARAM_INT);
3297
                    break;
3298
                case 'ids':
3299
                    $value = clean_param($params['value'], PARAM_SEQUENCE);
3300
                    break;
3301
                case 'shortname':
3302
                    $value = clean_param($params['value'], PARAM_TEXT);
3303
                    break;
3304
                case 'idnumber':
3305
                    $value = clean_param($params['value'], PARAM_RAW);
3306
                    break;
3307
                default:
3308
                    throw new invalid_parameter_exception('Invalid field name');
3309
            }
3310
 
3311
            if ($params['field'] === 'ids') {
3312
                // Preload categories to avoid loading one at a time.
3313
                $courseids = explode(',', $value);
3314
                list ($listsql, $listparams) = $DB->get_in_or_equal($courseids);
3315
                $categoryids = $DB->get_fieldset_sql("
3316
                        SELECT DISTINCT cc.id
3317
                          FROM {course} c
3318
                          JOIN {course_categories} cc ON cc.id = c.category
3319
                         WHERE c.id $listsql", $listparams);
3320
                core_course_category::get_many($categoryids);
3321
 
3322
                // Load and validate all courses. This is called because it loads the courses
3323
                // more efficiently.
3324
                list ($courses, $warnings) = util::validate_courses($courseids, [],
3325
                        false, true);
1441 ariadna 3326
            } else if ($params['field'] === 'sectionid') {
3327
                $courseid = $DB->get_field('course_sections', 'course', ['id' => $value]);
3328
                $courses = $courseid ? [$DB->get_record('course', ['id' => $courseid])] : [];
1 efrain 3329
            } else {
3330
                $courses = $DB->get_records('course', array($params['field'] => $value), 'id ASC');
3331
            }
3332
        }
3333
 
3334
        $iscommapiavailable = \core_communication\api::is_available();
3335
 
3336
        $coursesdata = array();
3337
        foreach ($courses as $course) {
3338
            $context = context_course::instance($course->id);
3339
            $canupdatecourse = has_capability('moodle/course:update', $context);
3340
            $canviewhiddencourses = has_capability('moodle/course:viewhiddencourses', $context);
3341
 
3342
            // Check if the course is visible in the site for the user.
3343
            if (!$course->visible and !$canviewhiddencourses and !$canupdatecourse) {
3344
                continue;
3345
            }
3346
            // Get the public course information, even if we are not enrolled.
3347
            $courseinlist = new core_course_list_element($course);
3348
 
3349
            // Now, check if we have access to the course, unless it was already checked.
3350
            try {
3351
                if (empty($course->contextvalidated)) {
3352
                    self::validate_context($context);
3353
                }
3354
            } catch (Exception $e) {
3355
                // User can not access the course, check if they can see the public information about the course and return it.
3356
                if (core_course_category::can_view_course_info($course)) {
3357
                    $coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context);
3358
                }
3359
                continue;
3360
            }
3361
            $coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context);
3362
            // Return information for any user that can access the course.
3363
            $coursefields = array('format', 'showgrades', 'newsitems', 'startdate', 'enddate', 'maxbytes', 'showreports', 'visible',
3364
                'groupmode', 'groupmodeforce', 'defaultgroupingid', 'enablecompletion', 'completionnotify', 'lang', 'theme',
3365
                'marker');
3366
 
3367
            // Course filters.
3368
            $coursesdata[$course->id]['filters'] = filter_get_available_in_context($context);
3369
 
3370
            // Information for managers only.
3371
            if ($canupdatecourse) {
3372
                $managerfields = array('idnumber', 'legacyfiles', 'calendartype', 'timecreated', 'timemodified', 'requested',
3373
                    'cacherev');
3374
                $coursefields = array_merge($coursefields, $managerfields);
3375
            }
3376
 
3377
            // Populate fields.
3378
            foreach ($coursefields as $field) {
3379
                $coursesdata[$course->id][$field] = $course->{$field};
3380
            }
3381
 
3382
            // Clean lang and auth fields for external functions (it may content uninstalled themes or language packs).
3383
            if (isset($coursesdata[$course->id]['theme'])) {
3384
                $coursesdata[$course->id]['theme'] = clean_param($coursesdata[$course->id]['theme'], PARAM_THEME);
3385
            }
3386
            if (isset($coursesdata[$course->id]['lang'])) {
3387
                $coursesdata[$course->id]['lang'] = clean_param($coursesdata[$course->id]['lang'], PARAM_LANG);
3388
            }
3389
 
3390
            $courseformatoptions = course_get_format($course)->get_config_for_external();
3391
            foreach ($courseformatoptions as $key => $value) {
3392
                $coursesdata[$course->id]['courseformatoptions'][] = array(
3393
                    'name' => $key,
3394
                    'value' => $value
3395
                );
3396
            }
3397
 
3398
            // Communication tools for the course.
3399
            if ($iscommapiavailable) {
3400
                $communication = \core_communication\api::load_by_instance(
3401
                    context: $context,
3402
                    component: 'core_course',
3403
                    instancetype: 'coursecommunication',
3404
                    instanceid: $course->id
3405
                );
3406
                if ($communication->get_provider()) {
3407
                    $coursesdata[$course->id]['communicationroomname'] = \core_external\util::format_string($communication->get_room_name(), $context);
3408
                    // This will be usually an URL, however, it is better to consider that can be anything a plugin might return, this is why we will use PARAM_RAW.
3409
                    $coursesdata[$course->id]['communicationroomurl'] = $communication->get_communication_room_url();
3410
                }
3411
            }
3412
        }
3413
 
3414
        return array(
3415
            'courses' => $coursesdata,
3416
            'warnings' => $warnings
3417
        );
3418
    }
3419
 
3420
    /**
3421
     * Returns description of method result value
3422
     *
3423
     * @return \core_external\external_description
3424
     * @since Moodle 3.2
3425
     */
3426
    public static function get_courses_by_field_returns() {
3427
        // Course structure, including not only public viewable fields.
3428
        return new external_single_structure(
3429
            array(
3430
                'courses' => new external_multiple_structure(self::get_course_structure(false), 'Course'),
3431
                'warnings' => new external_warnings()
3432
            )
3433
        );
3434
    }
3435
 
3436
    /**
3437
     * Returns description of method parameters
3438
     *
3439
     * @return external_function_parameters
3440
     * @since Moodle 3.2
3441
     */
3442
    public static function check_updates_parameters() {
3443
        return new external_function_parameters(
3444
            array(
3445
                'courseid' => new external_value(PARAM_INT, 'Course id to check'),
3446
                'tocheck' => new external_multiple_structure(
3447
                    new external_single_structure(
3448
                        array(
3449
                            'contextlevel' => new external_value(PARAM_ALPHA, 'The context level for the file location.
3450
                                                                                Only module supported right now.'),
3451
                            'id' => new external_value(PARAM_INT, 'Context instance id'),
3452
                            'since' => new external_value(PARAM_INT, 'Check updates since this time stamp'),
3453
                        )
3454
                    ),
3455
                    'Instances to check'
3456
                ),
3457
                'filter' => new external_multiple_structure(
3458
                    new external_value(PARAM_ALPHANUM, 'Area name: configuration, fileareas, completion, ratings, comments,
3459
                                                        gradeitems, outcomes'),
3460
                    'Check only for updates in these areas', VALUE_DEFAULT, array()
3461
                )
3462
            )
3463
        );
3464
    }
3465
 
3466
    /**
3467
     * Check if there is updates affecting the user for the given course and contexts.
3468
     * Right now only modules are supported.
3469
     * This WS calls mod_check_updates_since for each module to check if there is any update the user should we aware of.
3470
     *
3471
     * @param int $courseid the list of modules to check
3472
     * @param array $tocheck the list of modules to check
3473
     * @param array $filter check only for updates in these areas
3474
     * @return array list of updates and warnings
3475
     * @throws moodle_exception
3476
     * @since Moodle 3.2
3477
     */
3478
    public static function check_updates($courseid, $tocheck, $filter = array()) {
3479
        global $CFG, $DB;
3480
        require_once($CFG->dirroot . "/course/lib.php");
3481
 
3482
        $params = self::validate_parameters(
3483
            self::check_updates_parameters(),
3484
            array(
3485
                'courseid' => $courseid,
3486
                'tocheck' => $tocheck,
3487
                'filter' => $filter,
3488
            )
3489
        );
3490
 
3491
        $course = get_course($params['courseid']);
3492
        $context = context_course::instance($course->id);
3493
        self::validate_context($context);
3494
 
3495
        list($instances, $warnings) = course_check_updates($course, $params['tocheck'], $filter);
3496
 
3497
        $instancesformatted = array();
3498
        foreach ($instances as $instance) {
3499
            $updates = array();
3500
            foreach ($instance['updates'] as $name => $data) {
3501
                if (empty($data->updated)) {
3502
                    continue;
3503
                }
3504
                $updatedata = array(
3505
                    'name' => $name,
3506
                );
3507
                if (!empty($data->timeupdated)) {
3508
                    $updatedata['timeupdated'] = $data->timeupdated;
3509
                }
3510
                if (!empty($data->itemids)) {
3511
                    $updatedata['itemids'] = $data->itemids;
3512
                }
3513
                $updates[] = $updatedata;
3514
            }
3515
            if (!empty($updates)) {
3516
                $instancesformatted[] = array(
3517
                    'contextlevel' => $instance['contextlevel'],
3518
                    'id' => $instance['id'],
3519
                    'updates' => $updates
3520
                );
3521
            }
3522
        }
3523
 
3524
        return array(
3525
            'instances' => $instancesformatted,
3526
            'warnings' => $warnings
3527
        );
3528
    }
3529
 
3530
    /**
3531
     * Returns description of method result value
3532
     *
3533
     * @return \core_external\external_description
3534
     * @since Moodle 3.2
3535
     */
3536
    public static function check_updates_returns() {
3537
        return new external_single_structure(
3538
            array(
3539
                'instances' => new external_multiple_structure(
3540
                    new external_single_structure(
3541
                        array(
3542
                            'contextlevel' => new external_value(PARAM_ALPHA, 'The context level'),
3543
                            'id' => new external_value(PARAM_INT, 'Instance id'),
3544
                            'updates' => new external_multiple_structure(
3545
                                new external_single_structure(
3546
                                    array(
3547
                                        'name' => new external_value(PARAM_ALPHANUMEXT, 'Name of the area updated.'),
3548
                                        'timeupdated' => new external_value(PARAM_INT, 'Last time was updated', VALUE_OPTIONAL),
3549
                                        'itemids' => new external_multiple_structure(
3550
                                            new external_value(PARAM_INT, 'Instance id'),
3551
                                            'The ids of the items updated',
3552
                                            VALUE_OPTIONAL
3553
                                        )
3554
                                    )
3555
                                )
3556
                            )
3557
                        )
3558
                    )
3559
                ),
3560
                'warnings' => new external_warnings()
3561
            )
3562
        );
3563
    }
3564
 
3565
    /**
3566
     * Returns description of method parameters
3567
     *
3568
     * @return external_function_parameters
3569
     * @since Moodle 3.3
3570
     */
3571
    public static function get_updates_since_parameters() {
3572
        return new external_function_parameters(
3573
            array(
3574
                'courseid' => new external_value(PARAM_INT, 'Course id to check'),
3575
                'since' => new external_value(PARAM_INT, 'Check updates since this time stamp'),
3576
                'filter' => new external_multiple_structure(
3577
                    new external_value(PARAM_ALPHANUM, 'Area name: configuration, fileareas, completion, ratings, comments,
3578
                                                        gradeitems, outcomes'),
3579
                    'Check only for updates in these areas', VALUE_DEFAULT, array()
3580
                )
3581
            )
3582
        );
3583
    }
3584
 
3585
    /**
3586
     * Check if there are updates affecting the user for the given course since the given time stamp.
3587
     *
3588
     * This function is a wrapper of self::check_updates for retrieving all the updates since a given time for all the activities.
3589
     *
3590
     * @param int $courseid the list of modules to check
3591
     * @param int $since check updates since this time stamp
3592
     * @param array $filter check only for updates in these areas
3593
     * @return array list of updates and warnings
3594
     * @throws moodle_exception
3595
     * @since Moodle 3.3
3596
     */
3597
    public static function get_updates_since($courseid, $since, $filter = array()) {
3598
        global $CFG, $DB;
3599
 
3600
        $params = self::validate_parameters(
3601
            self::get_updates_since_parameters(),
3602
            array(
3603
                'courseid' => $courseid,
3604
                'since' => $since,
3605
                'filter' => $filter,
3606
            )
3607
        );
3608
 
3609
        $course = get_course($params['courseid']);
3610
        $modinfo = get_fast_modinfo($course);
3611
        $tocheck = array();
3612
 
3613
        // Retrieve all the visible course modules for the current user.
3614
        $cms = $modinfo->get_cms();
3615
        foreach ($cms as $cm) {
3616
            if (!$cm->uservisible) {
3617
                continue;
3618
            }
3619
            $tocheck[] = array(
3620
                'id' => $cm->id,
3621
                'contextlevel' => 'module',
3622
                'since' => $params['since'],
3623
            );
3624
        }
3625
 
3626
        return self::check_updates($course->id, $tocheck, $params['filter']);
3627
    }
3628
 
3629
    /**
3630
     * Returns description of method result value
3631
     *
3632
     * @return \core_external\external_description
3633
     * @since Moodle 3.3
3634
     */
3635
    public static function get_updates_since_returns() {
3636
        return self::check_updates_returns();
3637
    }
3638
 
3639
    /**
1441 ariadna 3640
     * Mark the edit_module as deprecated.
3641
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
3642
     * @return bool
3643
     */
3644
    public static function edit_module_is_deprecated() {
3645
        return true;
3646
    }
3647
 
3648
    /**
1 efrain 3649
     * Parameters for function edit_module()
3650
     *
1441 ariadna 3651
     * @deprecated since 5.0
3652
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
1 efrain 3653
     * @return external_function_parameters
3654
     */
3655
    public static function edit_module_parameters() {
3656
        return new external_function_parameters(
3657
            array(
3658
                'action' => new external_value(PARAM_ALPHA,
3659
                    'action: hide, show, stealth, duplicate, delete, moveleft, moveright, group...', VALUE_REQUIRED),
3660
                'id' => new external_value(PARAM_INT, 'course module id', VALUE_REQUIRED),
3661
                'sectionreturn' => new external_value(PARAM_INT, 'section to return to', VALUE_DEFAULT, null),
3662
            ));
3663
    }
3664
 
3665
    /**
3666
     * Performs one of the edit module actions and return new html for AJAX
3667
     *
3668
     * Returns html to replace the current module html with, for example:
3669
     * - empty string for "delete" action,
3670
     * - two modules html for "duplicate" action
3671
     * - updated module html for everything else
3672
     *
3673
     * Throws exception if operation is not permitted/possible
3674
     *
1441 ariadna 3675
     * @deprecated since 5.0
3676
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
1 efrain 3677
     * @param string $action
3678
     * @param int $id
3679
     * @param null|int $sectionreturn
3680
     * @return string
3681
     */
3682
    public static function edit_module($action, $id, $sectionreturn = null) {
3683
        global $PAGE, $DB;
3684
        // Validate and normalize parameters.
3685
        $params = self::validate_parameters(self::edit_module_parameters(),
3686
            array('action' => $action, 'id' => $id, 'sectionreturn' => $sectionreturn));
3687
        $action = $params['action'];
3688
        $id = $params['id'];
3689
        $sectionreturn = $params['sectionreturn'];
3690
 
3691
        // Set of permissions an editing user may have.
3692
        $contextarray = [
3693
                'moodle/course:update',
3694
                'moodle/course:manageactivities',
3695
                'moodle/course:activityvisibility',
3696
                'moodle/course:sectionvisibility',
3697
                'moodle/course:movesections',
3698
                'moodle/course:setcurrentsection',
3699
        ];
3700
        $PAGE->set_other_editing_capability($contextarray);
3701
 
3702
        list($course, $cm) = get_course_and_cm_from_cmid($id);
3703
        $modcontext = context_module::instance($cm->id);
3704
        $coursecontext = context_course::instance($course->id);
3705
        self::validate_context($modcontext);
3706
        $format = course_get_format($course);
3707
        if (!is_null($sectionreturn)) {
3708
            $format->set_sectionnum($sectionreturn);
3709
        }
3710
        $renderer = $format->get_renderer($PAGE);
3711
 
3712
        switch($action) {
3713
            case 'hide':
3714
            case 'show':
3715
            case 'stealth':
3716
                require_capability('moodle/course:activityvisibility', $modcontext);
3717
                $visible = ($action === 'hide') ? 0 : 1;
3718
                $visibleoncoursepage = ($action === 'stealth') ? 0 : 1;
3719
                set_coursemodule_visible($id, $visible, $visibleoncoursepage);
3720
                \core\event\course_module_updated::create_from_cm($cm, $modcontext)->trigger();
3721
                break;
3722
            case 'duplicate':
3723
                require_capability('moodle/course:manageactivities', $coursecontext);
3724
                require_capability('moodle/backup:backuptargetimport', $coursecontext);
3725
                require_capability('moodle/restore:restoretargetimport', $coursecontext);
3726
                if (!course_allowed_module($course, $cm->modname)) {
3727
                    throw new moodle_exception('No permission to create that activity');
3728
                }
3729
                if ($newcm = duplicate_module($course, $cm)) {
3730
 
3731
                    $modinfo = $format->get_modinfo();
3732
                    $section = $modinfo->get_section_info($newcm->sectionnum);
3733
                    $cm = $modinfo->get_cm($id);
3734
 
3735
                    // Get both original and new element html.
3736
                    $result = $renderer->course_section_updated_cm_item($format, $section, $cm);
3737
                    $result .= $renderer->course_section_updated_cm_item($format, $section, $newcm);
3738
                    return $result;
3739
                }
3740
                break;
3741
            case 'groupsseparate':
3742
            case 'groupsvisible':
3743
            case 'groupsnone':
3744
                require_capability('moodle/course:manageactivities', $modcontext);
3745
                if ($action === 'groupsseparate') {
3746
                    $newgroupmode = SEPARATEGROUPS;
3747
                } else if ($action === 'groupsvisible') {
3748
                    $newgroupmode = VISIBLEGROUPS;
3749
                } else {
3750
                    $newgroupmode = NOGROUPS;
3751
                }
3752
                if (set_coursemodule_groupmode($cm->id, $newgroupmode)) {
3753
                    \core\event\course_module_updated::create_from_cm($cm, $modcontext)->trigger();
3754
                }
3755
                break;
3756
            case 'moveleft':
3757
            case 'moveright':
3758
                require_capability('moodle/course:manageactivities', $modcontext);
3759
                $indent = $cm->indent + (($action === 'moveright') ? 1 : -1);
3760
                if ($cm->indent >= 0) {
3761
                    $DB->update_record('course_modules', array('id' => $cm->id, 'indent' => $indent));
3762
                    rebuild_course_cache($cm->course);
3763
                }
3764
                break;
3765
            case 'delete':
3766
                require_capability('moodle/course:manageactivities', $modcontext);
3767
                course_delete_module($cm->id, true);
3768
                return '';
3769
            default:
3770
                throw new coding_exception('Unrecognised action');
3771
        }
3772
 
3773
        $modinfo = $format->get_modinfo();
3774
        $section = $modinfo->get_section_info($cm->sectionnum);
3775
        $cm = $modinfo->get_cm($id);
3776
        return $renderer->course_section_updated_cm_item($format, $section, $cm);
3777
    }
3778
 
3779
    /**
3780
     * Return structure for edit_module()
3781
     *
1441 ariadna 3782
     * @deprecated since 5.0
3783
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
1 efrain 3784
     * @return \core_external\external_description
3785
     */
3786
    public static function edit_module_returns() {
3787
        return new external_value(PARAM_RAW, 'html to replace the current module with');
3788
    }
3789
 
3790
    /**
1441 ariadna 3791
     * Mark the get_module as deprecated.
3792
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
3793
     * @return bool
3794
     */
3795
    public static function get_module_is_deprecated() {
3796
        return true;
3797
    }
3798
 
3799
    /**
1 efrain 3800
     * Parameters for function get_module()
3801
     *
1441 ariadna 3802
     * @deprecated since 5.0
3803
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
1 efrain 3804
     * @return external_function_parameters
3805
     */
3806
    public static function get_module_parameters() {
3807
        return new external_function_parameters(
3808
            array(
3809
                'id' => new external_value(PARAM_INT, 'course module id', VALUE_REQUIRED),
3810
                'sectionreturn' => new external_value(PARAM_INT, 'section to return to', VALUE_DEFAULT, null),
3811
            ));
3812
    }
3813
 
3814
    /**
3815
     * Returns html for displaying one activity module on course page
3816
     *
1441 ariadna 3817
     * @deprecated since 5.0
3818
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
1 efrain 3819
     * @param int $id
3820
     * @param null|int $sectionreturn
3821
     * @return string
3822
     */
3823
    public static function get_module($id, $sectionreturn = null) {
3824
        global $PAGE;
3825
        // Validate and normalize parameters.
3826
        $params = self::validate_parameters(self::get_module_parameters(),
3827
            array('id' => $id, 'sectionreturn' => $sectionreturn));
3828
        $id = $params['id'];
3829
        $sectionreturn = $params['sectionreturn'];
3830
 
3831
        // Set of permissions an editing user may have.
3832
        $contextarray = [
3833
            'moodle/course:update',
3834
            'moodle/course:manageactivities',
3835
            'moodle/course:activityvisibility',
3836
            'moodle/course:sectionvisibility',
3837
            'moodle/course:movesections',
3838
            'moodle/course:setcurrentsection',
3839
        ];
3840
        $PAGE->set_other_editing_capability($contextarray);
3841
 
3842
        // Validate access to the course (note, this is html for the course view page, we don't validate access to the module).
3843
        list($course, $cm) = get_course_and_cm_from_cmid($id);
3844
        self::validate_context(context_course::instance($course->id));
3845
 
3846
        $format = course_get_format($course);
3847
        if (!is_null($sectionreturn)) {
3848
            $format->set_sectionnum($sectionreturn);
3849
        }
3850
        $renderer = $format->get_renderer($PAGE);
3851
 
3852
        $modinfo = $format->get_modinfo();
3853
        $section = $modinfo->get_section_info($cm->sectionnum);
3854
        return $renderer->course_section_updated_cm_item($format, $section, $cm);
3855
    }
3856
 
3857
    /**
3858
     * Return structure for get_module()
3859
     *
1441 ariadna 3860
     * @deprecated since 5.0
3861
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
1 efrain 3862
     * @return \core_external\external_description
3863
     */
3864
    public static function get_module_returns() {
3865
        return new external_value(PARAM_RAW, 'html to replace the current module with');
3866
    }
3867
 
3868
    /**
1441 ariadna 3869
     * Mark the edit_section as deprecated.
3870
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
3871
     * @return bool
3872
     */
3873
    public static function edit_section_is_deprecated() {
3874
        return true;
3875
    }
3876
 
3877
    /**
1 efrain 3878
     * Parameters for function edit_section()
3879
     *
1441 ariadna 3880
     * @deprecated since 5.0
3881
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
1 efrain 3882
     * @return external_function_parameters
3883
     */
3884
    public static function edit_section_parameters() {
3885
        return new external_function_parameters(
3886
            array(
3887
                'action' => new external_value(PARAM_ALPHA, 'action: hide, show, stealth, setmarker, removemarker', VALUE_REQUIRED),
3888
                'id' => new external_value(PARAM_INT, 'course section id', VALUE_REQUIRED),
3889
                'sectionreturn' => new external_value(PARAM_INT, 'section to return to', VALUE_DEFAULT, null),
3890
            ));
3891
    }
3892
 
3893
    /**
3894
     * Performs one of the edit section actions
3895
     *
1441 ariadna 3896
     * @deprecated since 5.0
3897
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
1 efrain 3898
     * @param string $action
3899
     * @param int $id section id
3900
     * @param int $sectionreturn section to return to
3901
     * @return string
3902
     */
3903
    public static function edit_section($action, $id, $sectionreturn) {
3904
        global $DB;
3905
        // Validate and normalize parameters.
3906
        $params = self::validate_parameters(self::edit_section_parameters(),
3907
            array('action' => $action, 'id' => $id, 'sectionreturn' => $sectionreturn));
3908
        $action = $params['action'];
3909
        $id = $params['id'];
3910
        $sr = $params['sectionreturn'];
3911
 
3912
        $section = $DB->get_record('course_sections', array('id' => $id), '*', MUST_EXIST);
3913
        $coursecontext = context_course::instance($section->course);
3914
        self::validate_context($coursecontext);
3915
 
3916
        $rv = course_get_format($section->course)->section_action($section, $action, $sectionreturn);
3917
        if ($rv) {
3918
            return json_encode($rv);
3919
        } else {
3920
            return null;
3921
        }
3922
    }
3923
 
3924
    /**
3925
     * Return structure for edit_section()
3926
     *
1441 ariadna 3927
     * @deprecated since 5.0
3928
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
1 efrain 3929
     * @return \core_external\external_description
3930
     */
3931
    public static function edit_section_returns() {
3932
        return new external_value(PARAM_RAW, 'Additional data for javascript (JSON-encoded string)');
3933
    }
3934
 
3935
    /**
3936
     * Returns description of method parameters
3937
     *
3938
     * @return external_function_parameters
3939
     */
3940
    public static function get_enrolled_courses_by_timeline_classification_parameters() {
3941
        return new external_function_parameters(
3942
            array(
3943
                'classification' => new external_value(PARAM_ALPHA, 'future, inprogress, or past'),
3944
                'limit' => new external_value(PARAM_INT, 'Result set limit', VALUE_DEFAULT, 0),
3945
                'offset' => new external_value(PARAM_INT, 'Result set offset', VALUE_DEFAULT, 0),
3946
                'sort' => new external_value(PARAM_TEXT, 'Sort string', VALUE_DEFAULT, null),
3947
                'customfieldname' => new external_value(PARAM_ALPHANUMEXT, 'Used when classification = customfield',
3948
                    VALUE_DEFAULT, null),
3949
                'customfieldvalue' => new external_value(PARAM_RAW, 'Used when classification = customfield',
3950
                    VALUE_DEFAULT, null),
3951
                'searchvalue' => new external_value(PARAM_RAW, 'The value a user wishes to search against',
3952
                    VALUE_DEFAULT, null),
3953
                'requiredfields' => new core_external\external_multiple_structure(
3954
                    new external_value(PARAM_ALPHANUMEXT, 'Field name to be included from the results', VALUE_DEFAULT),
3955
                    'Array of the only field names that need to be returned. If empty, all fields will be returned.',
3956
                    VALUE_DEFAULT, []
3957
                ),
3958
            )
3959
        );
3960
    }
3961
 
3962
    /**
3963
     * Get courses matching the given timeline classification.
3964
     *
3965
     * NOTE: The offset applies to the unfiltered full set of courses before the classification
3966
     * filtering is done.
3967
     * E.g.
3968
     * If the user is enrolled in 5 courses:
3969
     * c1, c2, c3, c4, and c5
3970
     * And c4 and c5 are 'future' courses
3971
     *
3972
     * If a request comes in for future courses with an offset of 1 it will mean that
3973
     * c1 is skipped (because the offset applies *before* the classification filtering)
3974
     * and c4 and c5 will be return.
3975
     *
3976
     * @param string $classification past, inprogress, or future
3977
     * @param int $limit Result set limit
3978
     * @param int $offset Offset the full course set before timeline classification is applied
3979
     * @param string|null $sort SQL sort string for results
3980
     * @param string|null $customfieldname
3981
     * @param string|null $customfieldvalue
3982
     * @param string|null $searchvalue
3983
     * @param array $requiredfields Array of the only field names that need to be returned. If empty, all fields will be returned.
3984
     * @return array list of courses and warnings
3985
     */
3986
    public static function get_enrolled_courses_by_timeline_classification(
3987
        string $classification,
3988
        int $limit = 0,
3989
        int $offset = 0,
1441 ariadna 3990
        ?string $sort = null,
3991
        ?string $customfieldname = null,
3992
        ?string $customfieldvalue = null,
3993
        ?string $searchvalue = null,
1 efrain 3994
        array $requiredfields = []
3995
    ) {
3996
        global $CFG, $PAGE, $USER;
3997
        require_once($CFG->dirroot . '/course/lib.php');
3998
 
3999
        $params = self::validate_parameters(self::get_enrolled_courses_by_timeline_classification_parameters(),
4000
            array(
4001
                'classification' => $classification,
4002
                'limit' => $limit,
4003
                'offset' => $offset,
4004
                'sort' => $sort,
4005
                'customfieldvalue' => $customfieldvalue,
4006
                'searchvalue' => $searchvalue,
4007
                'requiredfields' => $requiredfields,
4008
            )
4009
        );
4010
 
4011
        $classification = $params['classification'];
4012
        $limit = $params['limit'];
4013
        $offset = $params['offset'];
4014
        $sort = $params['sort'];
4015
        $customfieldvalue = $params['customfieldvalue'];
4016
        $searchvalue = clean_param($params['searchvalue'], PARAM_TEXT);
4017
        $requiredfields = $params['requiredfields'];
4018
 
4019
        switch($classification) {
4020
            case COURSE_TIMELINE_ALLINCLUDINGHIDDEN:
4021
                break;
4022
            case COURSE_TIMELINE_ALL:
4023
                break;
4024
            case COURSE_TIMELINE_PAST:
4025
                break;
4026
            case COURSE_TIMELINE_INPROGRESS:
4027
                break;
4028
            case COURSE_TIMELINE_FUTURE:
4029
                break;
4030
            case COURSE_FAVOURITES:
4031
                break;
4032
            case COURSE_TIMELINE_HIDDEN:
4033
                break;
4034
            case COURSE_TIMELINE_SEARCH:
4035
                break;
4036
            case COURSE_CUSTOMFIELD:
4037
                break;
4038
            default:
4039
                throw new invalid_parameter_exception('Invalid classification');
4040
        }
4041
 
4042
        self::validate_context(context_user::instance($USER->id));
4043
        $exporterfields = array_keys(course_summary_exporter::define_properties());
4044
        // Get the required properties from the exporter fields based on the required fields.
4045
        $requiredproperties = array_intersect($exporterfields, $requiredfields);
4046
        // If the resulting required properties is empty, fall back to the exporter fields.
4047
        if (empty($requiredproperties)) {
4048
            $requiredproperties = $exporterfields;
4049
        }
4050
 
4051
        $fields = join(',', $requiredproperties);
4052
        $hiddencourses = get_hidden_courses_on_timeline();
4053
 
4054
        // If the timeline requires really all courses, get really all courses.
4055
        if ($classification == COURSE_TIMELINE_ALLINCLUDINGHIDDEN) {
4056
            $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields, COURSE_DB_QUERY_LIMIT);
4057
 
4058
            // Otherwise if the timeline requires the hidden courses then restrict the result to only $hiddencourses.
4059
        } else if ($classification == COURSE_TIMELINE_HIDDEN) {
4060
            $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields,
4061
                COURSE_DB_QUERY_LIMIT, $hiddencourses);
4062
 
4063
            // Otherwise get the requested courses and exclude the hidden courses.
4064
        } else if ($classification == COURSE_TIMELINE_SEARCH) {
4065
            // Prepare the search API options.
4066
            $searchcriteria['search'] = $searchvalue;
4067
            $options = ['idonly' => true];
4068
            $courses = course_get_enrolled_courses_for_logged_in_user_from_search(
4069
                0,
4070
                $offset,
4071
                $sort,
4072
                $fields,
4073
                COURSE_DB_QUERY_LIMIT,
4074
                $searchcriteria,
4075
                $options
4076
            );
4077
        } else {
4078
            $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields,
4079
                COURSE_DB_QUERY_LIMIT, [], $hiddencourses);
4080
        }
4081
 
4082
        $favouritecourseids = [];
4083
        $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($USER->id));
4084
        $favourites = $ufservice->find_favourites_by_type('core_course', 'courses');
4085
 
4086
        if ($favourites) {
4087
            $favouritecourseids = array_map(
4088
                function($favourite) {
4089
                    return $favourite->itemid;
4090
                }, $favourites);
4091
        }
4092
 
4093
        if ($classification == COURSE_FAVOURITES) {
4094
            list($filteredcourses, $processedcount) = course_filter_courses_by_favourites(
4095
                $courses,
4096
                $favouritecourseids,
4097
                $limit
4098
            );
4099
        } else if ($classification == COURSE_CUSTOMFIELD) {
4100
            list($filteredcourses, $processedcount) = course_filter_courses_by_customfield(
4101
                $courses,
4102
                $customfieldname,
4103
                $customfieldvalue,
4104
                $limit
4105
            );
4106
        } else {
4107
            list($filteredcourses, $processedcount) = course_filter_courses_by_timeline_classification(
4108
                $courses,
4109
                $classification,
4110
                $limit
4111
            );
4112
        }
4113
 
4114
        $renderer = $PAGE->get_renderer('core');
4115
        $formattedcourses = array_map(function($course) use ($renderer, $favouritecourseids) {
4116
            if ($course == null) {
4117
                return;
4118
            }
4119
            context_helper::preload_from_record($course);
4120
            $context = context_course::instance($course->id);
4121
            $isfavourite = false;
4122
            if (in_array($course->id, $favouritecourseids)) {
4123
                $isfavourite = true;
4124
            }
4125
            $exporter = new course_summary_exporter($course, ['context' => $context, 'isfavourite' => $isfavourite]);
4126
            return $exporter->export($renderer);
4127
        }, $filteredcourses);
4128
 
4129
        $formattedcourses = array_filter($formattedcourses, function($course) {
4130
            if ($course != null) {
4131
                return $course;
4132
            }
4133
        });
4134
 
4135
        return [
4136
            'courses' => $formattedcourses,
4137
            'nextoffset' => $offset + $processedcount
4138
        ];
4139
    }
4140
 
4141
    /**
4142
     * Returns description of method result value
4143
     *
4144
     * @return \core_external\external_description
4145
     */
4146
    public static function get_enrolled_courses_by_timeline_classification_returns() {
4147
        return new external_single_structure(
4148
            array(
4149
                'courses' => new external_multiple_structure(course_summary_exporter::get_read_structure(), 'Course'),
4150
                'nextoffset' => new external_value(PARAM_INT, 'Offset for the next request')
4151
            )
4152
        );
4153
    }
4154
 
4155
    /**
4156
     * Returns description of method parameters
4157
     *
4158
     * @return external_function_parameters
4159
     */
4160
    public static function set_favourite_courses_parameters() {
4161
        return new external_function_parameters(
4162
            array(
4163
                'courses' => new external_multiple_structure(
4164
                    new external_single_structure(
4165
                        array(
4166
                            'id' => new external_value(PARAM_INT, 'course ID'),
4167
                            'favourite' => new external_value(PARAM_BOOL, 'favourite status')
4168
                        )
4169
                    )
4170
                )
4171
            )
4172
        );
4173
    }
4174
 
4175
    /**
4176
     * Set the course favourite status for an array of courses.
4177
     *
4178
     * @param  array $courses List with course id's and favourite status.
4179
     * @return array Array with an array of favourite courses.
4180
     */
4181
    public static function set_favourite_courses(
4182
        array $courses
4183
    ) {
4184
        global $USER;
4185
 
4186
        $params = self::validate_parameters(self::set_favourite_courses_parameters(),
4187
            array(
4188
                'courses' => $courses
4189
            )
4190
        );
4191
 
4192
        $warnings = [];
4193
 
4194
        $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($USER->id));
4195
 
4196
        foreach ($params['courses'] as $course) {
4197
 
4198
            $warning = [];
4199
 
4200
            $favouriteexists = $ufservice->favourite_exists('core_course', 'courses', $course['id'],
4201
                    \context_course::instance($course['id']));
4202
 
4203
            if ($course['favourite']) {
4204
                if (!$favouriteexists) {
4205
                    try {
4206
                        $ufservice->create_favourite('core_course', 'courses', $course['id'],
4207
                                \context_course::instance($course['id']));
4208
                    } catch (Exception $e) {
4209
                        $warning['courseid'] = $course['id'];
4210
                        if ($e instanceof moodle_exception) {
4211
                            $warning['warningcode'] = $e->errorcode;
4212
                        } else {
4213
                            $warning['warningcode'] = $e->getCode();
4214
                        }
4215
                        $warning['message'] = $e->getMessage();
4216
                        $warnings[] = $warning;
4217
                        $warnings[] = $warning;
4218
                    }
4219
                } else {
4220
                    $warning['courseid'] = $course['id'];
4221
                    $warning['warningcode'] = 'coursealreadyfavourited';
4222
                    $warning['message'] = 'Course already favourited';
4223
                    $warnings[] = $warning;
4224
                }
4225
            } else {
4226
                if ($favouriteexists) {
4227
                    try {
4228
                        $ufservice->delete_favourite('core_course', 'courses', $course['id'],
4229
                                \context_course::instance($course['id']));
4230
                    } catch (Exception $e) {
4231
                        $warning['courseid'] = $course['id'];
4232
                        if ($e instanceof moodle_exception) {
4233
                            $warning['warningcode'] = $e->errorcode;
4234
                        } else {
4235
                            $warning['warningcode'] = $e->getCode();
4236
                        }
4237
                        $warning['message'] = $e->getMessage();
4238
                        $warnings[] = $warning;
4239
                        $warnings[] = $warning;
4240
                    }
4241
                } else {
4242
                    $warning['courseid'] = $course['id'];
4243
                    $warning['warningcode'] = 'cannotdeletefavourite';
4244
                    $warning['message'] = 'Could not delete favourite status for course';
4245
                    $warnings[] = $warning;
4246
                }
4247
            }
4248
        }
4249
 
4250
        return [
4251
            'warnings' => $warnings
4252
        ];
4253
    }
4254
 
4255
    /**
4256
     * Returns description of method result value
4257
     *
4258
     * @return \core_external\external_description
4259
     */
4260
    public static function set_favourite_courses_returns() {
4261
        return new external_single_structure(
4262
            array(
4263
                'warnings' => new external_warnings()
4264
            )
4265
        );
4266
    }
4267
 
4268
    /**
4269
     * Returns description of method parameters
4270
     *
4271
     * @return external_function_parameters
4272
     * @since Moodle 3.6
4273
     */
4274
    public static function get_recent_courses_parameters() {
4275
        return new external_function_parameters(
4276
            array(
4277
                'userid' => new external_value(PARAM_INT, 'id of the user, default to current user', VALUE_DEFAULT, 0),
4278
                'limit' => new external_value(PARAM_INT, 'result set limit', VALUE_DEFAULT, 0),
4279
                'offset' => new external_value(PARAM_INT, 'Result set offset', VALUE_DEFAULT, 0),
4280
                'sort' => new external_value(PARAM_TEXT, 'Sort string', VALUE_DEFAULT, null)
4281
            )
4282
        );
4283
    }
4284
 
4285
    /**
4286
     * Get last accessed courses adding additional course information like images.
4287
     *
4288
     * @param int $userid User id from which the courses will be obtained
4289
     * @param int $limit Restrict result set to this amount
4290
     * @param int $offset Skip this number of records from the start of the result set
4291
     * @param string|null $sort SQL string for sorting
4292
     * @return array List of courses
4293
     * @throws  invalid_parameter_exception
4294
     */
1441 ariadna 4295
    public static function get_recent_courses(int $userid = 0, int $limit = 0, int $offset = 0, ?string $sort = null) {
1 efrain 4296
        global $USER, $PAGE;
4297
 
4298
        if (empty($userid)) {
4299
            $userid = $USER->id;
4300
        }
4301
 
4302
        $params = self::validate_parameters(self::get_recent_courses_parameters(),
4303
            array(
4304
                'userid' => $userid,
4305
                'limit' => $limit,
4306
                'offset' => $offset,
4307
                'sort' => $sort
4308
            )
4309
        );
4310
 
4311
        $userid = $params['userid'];
4312
        $limit = $params['limit'];
4313
        $offset = $params['offset'];
4314
        $sort = $params['sort'];
4315
 
4316
        $usercontext = context_user::instance($userid);
4317
 
4318
        self::validate_context($usercontext);
4319
 
1441 ariadna 4320
        if ($userid != $USER->id && !has_capability('moodle/user:viewalldetails', $usercontext)) {
1 efrain 4321
            return array();
4322
        }
4323
 
4324
        $courses = course_get_recent_courses($userid, $limit, $offset, $sort);
4325
 
4326
        $renderer = $PAGE->get_renderer('core');
4327
 
4328
        $recentcourses = array_map(function($course) use ($renderer) {
4329
            context_helper::preload_from_record($course);
4330
            $context = context_course::instance($course->id);
4331
            $isfavourite = !empty($course->component);
4332
            $exporter = new course_summary_exporter($course, ['context' => $context, 'isfavourite' => $isfavourite]);
4333
            return $exporter->export($renderer);
4334
        }, $courses);
4335
 
4336
        return $recentcourses;
4337
    }
4338
 
4339
    /**
4340
     * Returns description of method result value
4341
     *
4342
     * @return \core_external\external_description
4343
     * @since Moodle 3.6
4344
     */
4345
    public static function get_recent_courses_returns() {
4346
        return new external_multiple_structure(course_summary_exporter::get_read_structure(), 'Courses');
4347
    }
4348
 
4349
    /**
4350
     * Returns description of method parameters
4351
     *
4352
     * @return external_function_parameters
4353
     */
4354
    public static function get_enrolled_users_by_cmid_parameters() {
4355
        return new external_function_parameters([
4356
            'cmid' => new external_value(PARAM_INT, 'id of the course module', VALUE_REQUIRED),
4357
            'groupid' => new external_value(PARAM_INT, 'id of the group', VALUE_DEFAULT, 0),
4358
            'onlyactive' => new external_value(PARAM_BOOL, 'whether to return only active users or all.',
4359
                VALUE_DEFAULT, false),
4360
        ]);
4361
    }
4362
 
4363
    /**
4364
     * Get all users in a course for a given cmid.
4365
     *
4366
     * @param int $cmid Course Module id from which the users will be obtained
4367
     * @param int $groupid Group id from which the users will be obtained
4368
     * @param bool $onlyactive Whether to return only the active enrolled users or all enrolled users in the course.
4369
     * @return array List of users
4370
     */
4371
    public static function get_enrolled_users_by_cmid(int $cmid, int $groupid = 0, bool $onlyactive = false) {
1441 ariadna 4372
        global $PAGE;
4373
 
1 efrain 4374
        $warnings = [];
4375
 
1441 ariadna 4376
        [
4377
            'cmid' => $cmid,
4378
            'groupid' => $groupid,
4379
            'onlyactive' => $onlyactive,
4380
        ] = self::validate_parameters(self::get_enrolled_users_by_cmid_parameters(), [
4381
            'cmid' => $cmid,
4382
            'groupid' => $groupid,
4383
            'onlyactive' => $onlyactive,
1 efrain 4384
        ]);
4385
 
4386
        list($course, $cm) = get_course_and_cm_from_cmid($cmid);
4387
        $coursecontext = context_course::instance($course->id);
4388
        self::validate_context($coursecontext);
4389
 
1441 ariadna 4390
        course_require_view_participants($coursecontext);
4391
 
1 efrain 4392
        $enrolledusers = get_enrolled_users($coursecontext, '', $groupid, 'u.*', null, 0, 0, $onlyactive);
4393
 
4394
        $users = array_map(function ($user) use ($PAGE) {
4395
            $user->fullname = fullname($user);
4396
            $userpicture = new user_picture($user);
4397
            $userpicture->size = 1;
4398
            $user->profileimage = $userpicture->get_url($PAGE)->out(false);
4399
            return $user;
4400
        }, $enrolledusers);
4401
        sort($users);
4402
 
4403
        return [
4404
            'users' => $users,
4405
            'warnings' => $warnings,
4406
        ];
4407
    }
4408
 
4409
    /**
4410
     * Returns description of method result value
4411
     *
4412
     * @return \core_external\external_description
4413
     */
4414
    public static function get_enrolled_users_by_cmid_returns() {
4415
        return new external_single_structure([
4416
            'users' => new external_multiple_structure(self::user_description()),
4417
            'warnings' => new external_warnings(),
4418
        ]);
4419
    }
4420
 
4421
    /**
4422
     * Create user return value description.
4423
     *
4424
     * @return \core_external\external_description
4425
     */
4426
    public static function user_description() {
4427
        $userfields = array(
4428
            'id'    => new external_value(core_user::get_property_type('id'), 'ID of the user'),
4429
            'profileimage' => new external_value(PARAM_URL, 'The location of the users larger image', VALUE_OPTIONAL),
4430
            'fullname' => new external_value(PARAM_TEXT, 'The full name of the user', VALUE_OPTIONAL),
4431
            'firstname'   => new external_value(
4432
                    core_user::get_property_type('firstname'),
4433
                        'The first name(s) of the user',
4434
                        VALUE_OPTIONAL),
4435
            'lastname'    => new external_value(
4436
                    core_user::get_property_type('lastname'),
4437
                        'The family name of the user',
4438
                        VALUE_OPTIONAL),
4439
        );
4440
        return new external_single_structure($userfields);
4441
    }
4442
 
4443
    /**
4444
     * Returns description of method parameters.
4445
     *
4446
     * @return external_function_parameters
4447
     */
4448
    public static function add_content_item_to_user_favourites_parameters() {
4449
        return new external_function_parameters([
4450
            'componentname' => new external_value(PARAM_TEXT,
4451
                'frankenstyle name of the component to which the content item belongs', VALUE_REQUIRED),
4452
            'contentitemid' => new external_value(PARAM_INT, 'id of the content item', VALUE_REQUIRED, '', NULL_NOT_ALLOWED)
4453
        ]);
4454
    }
4455
 
4456
    /**
4457
     * Add a content item to a user's favourites.
4458
     *
4459
     * @param string $componentname the name of the component from which this content item originates.
4460
     * @param int $contentitemid the id of the content item.
4461
     * @return stdClass the exporter content item.
4462
     */
4463
    public static function add_content_item_to_user_favourites(string $componentname, int $contentitemid) {
4464
        global $USER;
4465
 
4466
        [
4467
            'componentname' => $componentname,
4468
            'contentitemid' => $contentitemid,
4469
        ] = self::validate_parameters(self::add_content_item_to_user_favourites_parameters(),
4470
            [
4471
                'componentname' => $componentname,
4472
                'contentitemid' => $contentitemid,
4473
            ]
4474
        );
4475
 
4476
        self::validate_context(context_user::instance($USER->id));
4477
 
4478
        $contentitemservice = \core_course\local\factory\content_item_service_factory::get_content_item_service();
4479
 
4480
        return $contentitemservice->add_to_user_favourites($USER, $componentname, $contentitemid);
4481
    }
4482
 
4483
    /**
4484
     * Returns description of method result value.
4485
     *
4486
     * @return \core_external\external_description
4487
     */
4488
    public static function add_content_item_to_user_favourites_returns() {
4489
        return \core_course\local\exporters\course_content_item_exporter::get_read_structure();
4490
    }
4491
 
4492
    /**
4493
     * Returns description of method parameters.
4494
     *
4495
     * @return external_function_parameters
4496
     */
4497
    public static function remove_content_item_from_user_favourites_parameters() {
4498
        return new external_function_parameters([
4499
            'componentname' => new external_value(PARAM_TEXT,
4500
                'frankenstyle name of the component to which the content item belongs', VALUE_REQUIRED),
4501
            'contentitemid' => new external_value(PARAM_INT, 'id of the content item', VALUE_REQUIRED, '', NULL_NOT_ALLOWED),
4502
        ]);
4503
    }
4504
 
4505
    /**
4506
     * Remove a content item from a user's favourites.
4507
     *
4508
     * @param string $componentname the name of the component from which this content item originates.
4509
     * @param int $contentitemid the id of the content item.
4510
     * @return stdClass the exported content item.
4511
     */
4512
    public static function remove_content_item_from_user_favourites(string $componentname, int $contentitemid) {
4513
        global $USER;
4514
 
4515
        [
4516
            'componentname' => $componentname,
4517
            'contentitemid' => $contentitemid,
4518
        ] = self::validate_parameters(self::remove_content_item_from_user_favourites_parameters(),
4519
            [
4520
                'componentname' => $componentname,
4521
                'contentitemid' => $contentitemid,
4522
            ]
4523
        );
4524
 
4525
        self::validate_context(context_user::instance($USER->id));
4526
 
4527
        $contentitemservice = \core_course\local\factory\content_item_service_factory::get_content_item_service();
4528
 
4529
        return $contentitemservice->remove_from_user_favourites($USER, $componentname, $contentitemid);
4530
    }
4531
 
4532
    /**
4533
     * Returns description of method result value.
4534
     *
4535
     * @return \core_external\external_description
4536
     */
4537
    public static function remove_content_item_from_user_favourites_returns() {
4538
        return \core_course\local\exporters\course_content_item_exporter::get_read_structure();
4539
    }
4540
 
4541
    /**
4542
     * Returns description of method result value
4543
     *
4544
     * @return \core_external\external_description
4545
     */
4546
    public static function get_course_content_items_returns() {
4547
        return new external_single_structure([
4548
            'content_items' => new external_multiple_structure(
4549
                \core_course\local\exporters\course_content_item_exporter::get_read_structure()
4550
            ),
4551
        ]);
4552
    }
4553
 
4554
    /**
4555
     * Returns description of method parameters
4556
     *
4557
     * @return external_function_parameters
4558
     */
4559
    public static function get_course_content_items_parameters() {
4560
        return new external_function_parameters([
4561
            'courseid' => new external_value(PARAM_INT, 'ID of the course', VALUE_REQUIRED),
1441 ariadna 4562
            'sectionnum' => new external_value(PARAM_INT, 'Number of the section', VALUE_DEFAULT),
1 efrain 4563
        ]);
4564
    }
4565
 
4566
    /**
4567
     * Given a course ID fetch all accessible modules for that course
4568
     *
4569
     * @param int $courseid The course we want to fetch the modules for
1441 ariadna 4570
     * @param int|null $sectionnum The section we want to fetch the modules for
1 efrain 4571
     * @return array Contains array of modules and their metadata
4572
     */
1441 ariadna 4573
    public static function get_course_content_items(int $courseid, ?int $sectionnum = null) {
1 efrain 4574
        global $USER;
4575
 
4576
        [
4577
            'courseid' => $courseid,
1441 ariadna 4578
            'sectionnum' => $sectionnum,
1 efrain 4579
        ] = self::validate_parameters(self::get_course_content_items_parameters(), [
4580
            'courseid' => $courseid,
1441 ariadna 4581
            'sectionnum' => $sectionnum,
1 efrain 4582
        ]);
4583
 
4584
        $coursecontext = context_course::instance($courseid);
4585
        self::validate_context($coursecontext);
4586
        $course = get_course($courseid);
1441 ariadna 4587
        // Get section_info object with all delegation information.
4588
        $sectioninfo = get_fast_modinfo($course)->get_section_info($sectionnum);
1 efrain 4589
 
4590
        $contentitemservice = \core_course\local\factory\content_item_service_factory::get_content_item_service();
4591
 
1441 ariadna 4592
        $contentitems = $contentitemservice->get_content_items_for_user_in_course($USER, $course, [], $sectioninfo);
1 efrain 4593
        return ['content_items' => $contentitems];
4594
    }
4595
 
4596
    /**
4597
     * Returns description of method parameters.
4598
     *
4599
     * @return external_function_parameters
4600
     */
4601
    public static function toggle_activity_recommendation_parameters() {
4602
        return new external_function_parameters([
4603
            'area' => new external_value(PARAM_TEXT, 'The favourite area (itemtype)', VALUE_REQUIRED),
4604
            'id' => new external_value(PARAM_INT, 'id of the activity or whatever', VALUE_REQUIRED),
4605
        ]);
4606
    }
4607
 
4608
    /**
4609
     * Update the recommendation for an activity item.
4610
     *
4611
     * @param  string $area identifier for this activity.
4612
     * @param  int $id Associated id. This is needed in conjunction with the area to find the recommendation.
4613
     * @return array some warnings or something.
4614
     */
4615
    public static function toggle_activity_recommendation(string $area, int $id): array {
4616
        ['area' => $area, 'id' => $id] = self::validate_parameters(self::toggle_activity_recommendation_parameters(),
4617
                ['area' => $area, 'id' => $id]);
4618
 
4619
        $context = context_system::instance();
4620
        self::validate_context($context);
4621
 
4622
        require_capability('moodle/course:recommendactivity', $context);
4623
 
4624
        $manager = \core_course\local\factory\content_item_service_factory::get_content_item_service();
4625
 
4626
        $status = $manager->toggle_recommendation($area, $id);
4627
        return ['id' => $id, 'area' => $area, 'status' => $status];
4628
    }
4629
 
4630
    /**
4631
     * Returns warnings.
4632
     *
4633
     * @return \core_external\external_description
4634
     */
4635
    public static function toggle_activity_recommendation_returns() {
4636
        return new external_single_structure(
4637
            [
4638
                'id' => new external_value(PARAM_INT, 'id of the activity or whatever'),
4639
                'area' => new external_value(PARAM_TEXT, 'The favourite area (itemtype)'),
4640
                'status' => new external_value(PARAM_BOOL, 'If created or deleted'),
4641
            ]
4642
        );
4643
    }
4644
 
4645
    /**
4646
     * Returns description of method parameters
4647
     *
4648
     * @return external_function_parameters
4649
     */
4650
    public static function get_activity_chooser_footer_parameters() {
4651
        return new external_function_parameters([
4652
            'courseid' => new external_value(PARAM_INT, 'ID of the course', VALUE_REQUIRED),
4653
            'sectionid' => new external_value(PARAM_INT, 'ID of the section', VALUE_REQUIRED),
4654
        ]);
4655
    }
4656
 
4657
    /**
4658
     * Given a course ID we need to build up a footre for the chooser.
4659
     *
4660
     * @param int $courseid The course we want to fetch the modules for
4661
     * @param int $sectionid The section we want to fetch the modules for
4662
     * @return array
4663
     */
4664
    public static function get_activity_chooser_footer(int $courseid, int $sectionid) {
4665
        [
4666
            'courseid' => $courseid,
4667
            'sectionid' => $sectionid,
4668
        ] = self::validate_parameters(self::get_activity_chooser_footer_parameters(), [
4669
            'courseid' => $courseid,
4670
            'sectionid' => $sectionid,
4671
        ]);
4672
 
4673
        $coursecontext = context_course::instance($courseid);
4674
        self::validate_context($coursecontext);
4675
 
1441 ariadna 4676
        // The active plugin must be set, and be present on the site.
4677
        $activeplugin = clean_param(
4678
            get_config('core', 'activitychooseractivefooter'),
4679
            PARAM_COMPONENT,
4680
        );
1 efrain 4681
 
1441 ariadna 4682
        if (
4683
            $activeplugin !== COURSE_CHOOSER_FOOTER_NONE &&
4684
            !empty($activeplugin) &&
4685
            core_component::get_component_directory($activeplugin) !== null
4686
        ) {
1 efrain 4687
            $footerdata = component_callback($activeplugin, 'custom_chooser_footer', [$courseid, $sectionid]);
4688
            return [
4689
                'footer' => true,
4690
                'customfooterjs' => $footerdata->get_footer_js_file(),
4691
                'customfootertemplate' => $footerdata->get_footer_template(),
4692
                'customcarouseltemplate' => $footerdata->get_carousel_template(),
4693
            ];
4694
        } else {
4695
            return [
4696
                'footer' => false,
4697
            ];
4698
        }
4699
    }
4700
 
4701
    /**
4702
     * Returns description of method result value
4703
     *
4704
     * @return \core_external\external_description
4705
     */
4706
    public static function get_activity_chooser_footer_returns() {
4707
        return new external_single_structure(
4708
            [
4709
                'footer' => new external_value(PARAM_BOOL, 'Is a footer being return by this request?', VALUE_REQUIRED),
4710
                'customfooterjs' => new external_value(PARAM_RAW, 'The path to the plugin JS file', VALUE_OPTIONAL),
4711
                'customfootertemplate' => new external_value(PARAM_RAW, 'The prerendered footer', VALUE_OPTIONAL),
4712
                'customcarouseltemplate' => new external_value(PARAM_RAW, 'Either "" or the prerendered carousel page',
4713
                    VALUE_OPTIONAL),
4714
            ]
4715
        );
4716
    }
4717
}