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 * Wiki module external API.
19 *
20 * @package    mod_wiki
21 * @category   external
22 * @copyright  2015 Dani Palou <dani@moodle.com>
23 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 * @since      Moodle 3.1
25 */
26
27defined('MOODLE_INTERNAL') || die;
28
29require_once($CFG->libdir . '/externallib.php');
30require_once($CFG->dirroot . '/mod/wiki/lib.php');
31require_once($CFG->dirroot . '/mod/wiki/locallib.php');
32
33/**
34 * Wiki module external functions.
35 *
36 * @package    mod_wiki
37 * @category   external
38 * @copyright  2015 Dani Palou <dani@moodle.com>
39 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 * @since      Moodle 3.1
41 */
42class mod_wiki_external extends external_api {
43
44    /**
45     * Describes the parameters for get_wikis_by_courses.
46     *
47     * @return external_function_parameters
48     * @since Moodle 3.1
49     */
50    public static function get_wikis_by_courses_parameters() {
51        return new external_function_parameters (
52            array(
53                'courseids' => new external_multiple_structure(
54                    new external_value(PARAM_INT, 'Course ID'), 'Array of course ids.', VALUE_DEFAULT, array()
55                ),
56            )
57        );
58    }
59
60    /**
61     * Returns a list of wikis in a provided list of courses,
62     * if no list is provided all wikis that the user can view will be returned.
63     *
64     * @param array $courseids The courses IDs.
65     * @return array Containing a list of warnings and a list of wikis.
66     * @since Moodle 3.1
67     */
68    public static function get_wikis_by_courses($courseids = array()) {
69
70        $returnedwikis = array();
71        $warnings = array();
72
73        $params = self::validate_parameters(self::get_wikis_by_courses_parameters(), array('courseids' => $courseids));
74
75        $mycourses = array();
76        if (empty($params['courseids'])) {
77            $mycourses = enrol_get_my_courses();
78            $params['courseids'] = array_keys($mycourses);
79        }
80
81        // Ensure there are courseids to loop through.
82        if (!empty($params['courseids'])) {
83
84            list($courses, $warnings) = external_util::validate_courses($params['courseids'], $mycourses);
85
86            // Get the wikis in this course, this function checks users visibility permissions.
87            // We can avoid then additional validate_context calls.
88            $wikis = get_all_instances_in_courses('wiki', $courses);
89
90            foreach ($wikis as $wiki) {
91
92                $context = context_module::instance($wiki->coursemodule);
93
94                // Entry to return.
95                $module = array();
96
97                // First, we return information that any user can see in (or can deduce from) the web interface.
98                $module['id'] = $wiki->id;
99                $module['coursemodule'] = $wiki->coursemodule;
100                $module['course'] = $wiki->course;
101                $module['name']  = external_format_string($wiki->name, $context->id);
102
103                $viewablefields = [];
104                if (has_capability('mod/wiki:viewpage', $context)) {
105                    $options = array('noclean' => true);
106                    list($module['intro'], $module['introformat']) =
107                        external_format_text($wiki->intro, $wiki->introformat, $context->id, 'mod_wiki', 'intro', null, $options);
108                    $module['introfiles'] = external_util::get_area_files($context->id, 'mod_wiki', 'intro', false, false);
109
110                    $viewablefields = array('firstpagetitle', 'wikimode', 'defaultformat', 'forceformat', 'editbegin', 'editend',
111                                            'section', 'visible', 'groupmode', 'groupingid');
112                }
113
114                // Check additional permissions for returning optional private settings.
115                if (has_capability('moodle/course:manageactivities', $context)) {
116                    $additionalfields = array('timecreated', 'timemodified');
117                    $viewablefields = array_merge($viewablefields, $additionalfields);
118                }
119
120                foreach ($viewablefields as $field) {
121                    $module[$field] = $wiki->{$field};
122                }
123
124                // Check if user can add new pages.
125                $module['cancreatepages'] = wiki_can_create_pages($context);
126
127                $returnedwikis[] = $module;
128            }
129        }
130
131        $result = array();
132        $result['wikis'] = $returnedwikis;
133        $result['warnings'] = $warnings;
134        return $result;
135    }
136
137    /**
138     * Describes the get_wikis_by_courses return value.
139     *
140     * @return external_single_structure
141     * @since Moodle 3.1
142     */
143    public static function get_wikis_by_courses_returns() {
144
145        return new external_single_structure(
146            array(
147                'wikis' => new external_multiple_structure(
148                    new external_single_structure(
149                        array(
150                            'id' => new external_value(PARAM_INT, 'Wiki ID.'),
151                            'coursemodule' => new external_value(PARAM_INT, 'Course module ID.'),
152                            'course' => new external_value(PARAM_INT, 'Course ID.'),
153                            'name' => new external_value(PARAM_RAW, 'Wiki name.'),
154                            'intro' => new external_value(PARAM_RAW, 'Wiki intro.', VALUE_OPTIONAL),
155                            'introformat' => new external_format_value('Wiki intro format.', VALUE_OPTIONAL),
156                            'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL),
157                            'timecreated' => new external_value(PARAM_INT, 'Time of creation.', VALUE_OPTIONAL),
158                            'timemodified' => new external_value(PARAM_INT, 'Time of last modification.', VALUE_OPTIONAL),
159                            'firstpagetitle' => new external_value(PARAM_RAW, 'First page title.', VALUE_OPTIONAL),
160                            'wikimode' => new external_value(PARAM_TEXT, 'Wiki mode (individual, collaborative).', VALUE_OPTIONAL),
161                            'defaultformat' => new external_value(PARAM_TEXT, 'Wiki\'s default format (html, creole, nwiki).',
162                                                                            VALUE_OPTIONAL),
163                            'forceformat' => new external_value(PARAM_INT, '1 if format is forced, 0 otherwise.',
164                                                                            VALUE_OPTIONAL),
165                            'editbegin' => new external_value(PARAM_INT, 'Edit begin.', VALUE_OPTIONAL),
166                            'editend' => new external_value(PARAM_INT, 'Edit end.', VALUE_OPTIONAL),
167                            'section' => new external_value(PARAM_INT, 'Course section ID.', VALUE_OPTIONAL),
168                            'visible' => new external_value(PARAM_INT, '1 if visible, 0 otherwise.', VALUE_OPTIONAL),
169                            'groupmode' => new external_value(PARAM_INT, 'Group mode.', VALUE_OPTIONAL),
170                            'groupingid' => new external_value(PARAM_INT, 'Group ID.', VALUE_OPTIONAL),
171                            'cancreatepages' => new external_value(PARAM_BOOL, 'True if user can create pages.'),
172                        ), 'Wikis'
173                    )
174                ),
175                'warnings' => new external_warnings(),
176            )
177        );
178    }
179
180    /**
181     * Describes the parameters for view_wiki.
182     *
183     * @return external_function_parameters
184     * @since Moodle 3.1
185     */
186    public static function view_wiki_parameters() {
187        return new external_function_parameters (
188            array(
189                'wikiid' => new external_value(PARAM_INT, 'Wiki instance ID.')
190            )
191        );
192    }
193
194    /**
195     * Trigger the course module viewed event and update the module completion status.
196     *
197     * @param int $wikiid The wiki instance ID.
198     * @return array of warnings and status result.
199     * @since Moodle 3.1
200     */
201    public static function view_wiki($wikiid) {
202
203        $params = self::validate_parameters(self::view_wiki_parameters(),
204                                            array(
205                                                'wikiid' => $wikiid
206                                            ));
207        $warnings = array();
208
209        // Get wiki instance.
210        if (!$wiki = wiki_get_wiki($params['wikiid'])) {
211            throw new moodle_exception('incorrectwikiid', 'wiki');
212        }
213
214        // Permission validation.
215        list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
216        $context = context_module::instance($cm->id);
217        self::validate_context($context);
218
219        // Check if user can view this wiki.
220        // We don't use wiki_user_can_view because it requires to have a valid subwiki for the user.
221        if (!has_capability('mod/wiki:viewpage', $context)) {
222            throw new moodle_exception('cannotviewpage', 'wiki');
223        }
224
225        // Trigger course_module_viewed event and completion.
226        wiki_view($wiki, $course, $cm, $context);
227
228        $result = array();
229        $result['status'] = true;
230        $result['warnings'] = $warnings;
231        return $result;
232    }
233
234    /**
235     * Describes the view_wiki return value.
236     *
237     * @return external_single_structure
238     * @since Moodle 3.1
239     */
240    public static function view_wiki_returns() {
241        return new external_single_structure(
242            array(
243                'status' => new external_value(PARAM_BOOL, 'Status: true if success.'),
244                'warnings' => new external_warnings()
245            )
246        );
247    }
248
249    /**
250     * Describes the parameters for view_page.
251     *
252     * @return external_function_parameters
253     * @since Moodle 3.1
254     */
255    public static function view_page_parameters() {
256        return new external_function_parameters (
257            array(
258                'pageid' => new external_value(PARAM_INT, 'Wiki page ID.'),
259            )
260        );
261    }
262
263    /**
264     * Trigger the page viewed event and update the module completion status.
265     *
266     * @param int $pageid The page ID.
267     * @return array of warnings and status result.
268     * @since Moodle 3.1
269     * @throws moodle_exception if page is not valid.
270     */
271    public static function view_page($pageid) {
272
273        $params = self::validate_parameters(self::view_page_parameters(),
274                                            array(
275                                                'pageid' => $pageid
276                                            ));
277        $warnings = array();
278
279        // Get wiki page.
280        if (!$page = wiki_get_page($params['pageid'])) {
281            throw new moodle_exception('incorrectpageid', 'wiki');
282        }
283
284        // Get wiki instance.
285        if (!$wiki = wiki_get_wiki_from_pageid($params['pageid'])) {
286            throw new moodle_exception('incorrectwikiid', 'wiki');
287        }
288
289        // Permission validation.
290        list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
291        $context = context_module::instance($cm->id);
292        self::validate_context($context);
293
294        // Check if user can view this wiki.
295        if (!$subwiki = wiki_get_subwiki($page->subwikiid)) {
296            throw new moodle_exception('incorrectsubwikiid', 'wiki');
297        }
298        if (!wiki_user_can_view($subwiki, $wiki)) {
299            throw new moodle_exception('cannotviewpage', 'wiki');
300        }
301
302        // Trigger page_viewed event and completion.
303        wiki_page_view($wiki, $page, $course, $cm, $context);
304
305        $result = array();
306        $result['status'] = true;
307        $result['warnings'] = $warnings;
308        return $result;
309    }
310
311    /**
312     * Describes the view_page return value.
313     *
314     * @return external_single_structure
315     * @since Moodle 3.1
316     */
317    public static function view_page_returns() {
318        return new external_single_structure(
319            array(
320                'status' => new external_value(PARAM_BOOL, 'Status: true if success.'),
321                'warnings' => new external_warnings()
322            )
323        );
324    }
325
326    /**
327     * Describes the parameters for get_subwikis.
328     *
329     * @return external_function_parameters
330     * @since Moodle 3.1
331     */
332    public static function get_subwikis_parameters() {
333        return new external_function_parameters (
334            array(
335                'wikiid' => new external_value(PARAM_INT, 'Wiki instance ID.')
336            )
337        );
338    }
339
340    /**
341     * Returns the list of subwikis the user can see in a specific wiki.
342     *
343     * @param int $wikiid The wiki instance ID.
344     * @return array Containing a list of warnings and a list of subwikis.
345     * @since Moodle 3.1
346     */
347    public static function get_subwikis($wikiid) {
348        global $USER;
349
350        $warnings = array();
351
352        $params = self::validate_parameters(self::get_subwikis_parameters(), array('wikiid' => $wikiid));
353
354        // Get wiki instance.
355        if (!$wiki = wiki_get_wiki($params['wikiid'])) {
356            throw new moodle_exception('incorrectwikiid', 'wiki');
357        }
358
359        // Validate context and capabilities.
360        list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
361        $context = context_module::instance($cm->id);
362        self::validate_context($context);
363        require_capability('mod/wiki:viewpage', $context);
364
365        $returnedsubwikis = wiki_get_visible_subwikis($wiki, $cm, $context);
366        foreach ($returnedsubwikis as $subwiki) {
367            $subwiki->canedit = wiki_user_can_edit($subwiki);
368        }
369
370        $result = array();
371        $result['subwikis'] = $returnedsubwikis;
372        $result['warnings'] = $warnings;
373        return $result;
374    }
375
376    /**
377     * Describes the get_subwikis return value.
378     *
379     * @return external_single_structure
380     * @since Moodle 3.1
381     */
382    public static function get_subwikis_returns() {
383        return new external_single_structure(
384            array(
385                'subwikis' => new external_multiple_structure(
386                    new external_single_structure(
387                        array(
388                            'id' => new external_value(PARAM_INT, 'Subwiki ID.'),
389                            'wikiid' => new external_value(PARAM_INT, 'Wiki ID.'),
390                            'groupid' => new external_value(PARAM_RAW, 'Group ID.'),
391                            'userid' => new external_value(PARAM_INT, 'User ID.'),
392                            'canedit' => new external_value(PARAM_BOOL, 'True if user can edit the subwiki.'),
393                        ), 'Subwikis'
394                    )
395                ),
396                'warnings' => new external_warnings(),
397            )
398        );
399    }
400
401    /**
402     * Describes the parameters for get_subwiki_pages.
403     *
404     * @return external_function_parameters
405     * @since Moodle 3.1
406     */
407    public static function get_subwiki_pages_parameters() {
408        return new external_function_parameters (
409            array(
410                'wikiid' => new external_value(PARAM_INT, 'Wiki instance ID.'),
411                'groupid' => new external_value(PARAM_INT, 'Subwiki\'s group ID, -1 means current group. It will be ignored'
412                                        . ' if the wiki doesn\'t use groups.', VALUE_DEFAULT, -1),
413                'userid' => new external_value(PARAM_INT, 'Subwiki\'s user ID, 0 means current user. It will be ignored'
414                                        .' in collaborative wikis.', VALUE_DEFAULT, 0),
415                'options' => new external_single_structure(
416                            array(
417                                    'sortby' => new external_value(PARAM_ALPHA,
418                                            'Field to sort by (id, title, ...).', VALUE_DEFAULT, 'title'),
419                                    'sortdirection' => new external_value(PARAM_ALPHA,
420                                            'Sort direction: ASC or DESC.', VALUE_DEFAULT, 'ASC'),
421                                    'includecontent' => new external_value(PARAM_INT,
422                                            'Include each page contents or just the contents size.', VALUE_DEFAULT, 1),
423                            ), 'Options', VALUE_DEFAULT, array()),
424            )
425        );
426    }
427
428    /**
429     * Returns the list of pages from a specific subwiki.
430     *
431     * @param int $wikiid The wiki instance ID.
432     * @param int $groupid The group ID. If not defined, use current group.
433     * @param int $userid The user ID. If not defined, use current user.
434     * @param array $options Several options like sort by, sort direction, ...
435     * @return array Containing a list of warnings and a list of pages.
436     * @since Moodle 3.1
437     */
438    public static function get_subwiki_pages($wikiid, $groupid = -1, $userid = 0, $options = array()) {
439
440        $returnedpages = array();
441        $warnings = array();
442
443        $params = self::validate_parameters(self::get_subwiki_pages_parameters(),
444                                            array(
445                                                'wikiid' => $wikiid,
446                                                'groupid' => $groupid,
447                                                'userid' => $userid,
448                                                'options' => $options
449                                                )
450            );
451
452        // Get wiki instance.
453        if (!$wiki = wiki_get_wiki($params['wikiid'])) {
454            throw new moodle_exception('incorrectwikiid', 'wiki');
455        }
456        list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
457        $context = context_module::instance($cm->id);
458        self::validate_context($context);
459
460        // Determine groupid and userid to use.
461        list($groupid, $userid) = self::determine_group_and_user($cm, $wiki, $params['groupid'], $params['userid']);
462
463        // Get subwiki and validate it.
464        $subwiki = wiki_get_subwiki_by_group_and_user_with_validation($wiki, $groupid, $userid);
465
466        if ($subwiki === false) {
467            throw new moodle_exception('cannotviewpage', 'wiki');
468        } else if ($subwiki->id != -1) {
469
470            // Set sort param.
471            $options = $params['options'];
472            if (!empty($options['sortby'])) {
473                if ($options['sortdirection'] != 'ASC' && $options['sortdirection'] != 'DESC') {
474                    // Invalid sort direction. Use default.
475                    $options['sortdirection'] = 'ASC';
476                }
477                $sort = $options['sortby'] . ' ' . $options['sortdirection'];
478            }
479
480            $pages = wiki_get_page_list($subwiki->id, $sort);
481            $caneditpages = wiki_user_can_edit($subwiki);
482            $firstpage = wiki_get_first_page($subwiki->id);
483
484            foreach ($pages as $page) {
485                $retpage = array(
486                        'id' => $page->id,
487                        'subwikiid' => $page->subwikiid,
488                        'title' => external_format_string($page->title, $context->id),
489                        'timecreated' => $page->timecreated,
490                        'timemodified' => $page->timemodified,
491                        'timerendered' => $page->timerendered,
492                        'userid' => $page->userid,
493                        'pageviews' => $page->pageviews,
494                        'readonly' => $page->readonly,
495                        'caneditpage' => $caneditpages,
496                        'firstpage' => $page->id == $firstpage->id,
497                        'tags' => \core_tag\external\util::get_item_tags('mod_wiki', 'wiki_pages', $page->id),
498                    );
499
500                // Refresh page cached content if needed.
501                if ($page->timerendered + WIKI_REFRESH_CACHE_TIME < time()) {
502                    if ($content = wiki_refresh_cachedcontent($page)) {
503                        $page = $content['page'];
504                    }
505                }
506                list($cachedcontent, $contentformat) = external_format_text(
507                            $page->cachedcontent, FORMAT_HTML, $context->id, 'mod_wiki', 'attachments', $subwiki->id);
508
509                if ($options['includecontent']) {
510                    // Return the page content.
511                    $retpage['cachedcontent'] = $cachedcontent;
512                    $retpage['contentformat'] = $contentformat;
513                } else {
514                    // Return the size of the content.
515                    if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
516                        $retpage['contentsize'] = mb_strlen($cachedcontent, '8bit');
517                    } else {
518                        $retpage['contentsize'] = strlen($cachedcontent);
519                    }
520                }
521
522                $returnedpages[] = $retpage;
523            }
524        }
525
526        $result = array();
527        $result['pages'] = $returnedpages;
528        $result['warnings'] = $warnings;
529        return $result;
530    }
531
532    /**
533     * Describes the get_subwiki_pages return value.
534     *
535     * @return external_single_structure
536     * @since Moodle 3.1
537     */
538    public static function get_subwiki_pages_returns() {
539
540        return new external_single_structure(
541            array(
542                'pages' => new external_multiple_structure(
543                    new external_single_structure(
544                        array(
545                            'id' => new external_value(PARAM_INT, 'Page ID.'),
546                            'subwikiid' => new external_value(PARAM_INT, 'Page\'s subwiki ID.'),
547                            'title' => new external_value(PARAM_RAW, 'Page title.'),
548                            'timecreated' => new external_value(PARAM_INT, 'Time of creation.'),
549                            'timemodified' => new external_value(PARAM_INT, 'Time of last modification.'),
550                            'timerendered' => new external_value(PARAM_INT, 'Time of last renderization.'),
551                            'userid' => new external_value(PARAM_INT, 'ID of the user that last modified the page.'),
552                            'pageviews' => new external_value(PARAM_INT, 'Number of times the page has been viewed.'),
553                            'readonly' => new external_value(PARAM_INT, '1 if readonly, 0 otherwise.'),
554                            'caneditpage' => new external_value(PARAM_BOOL, 'True if user can edit the page.'),
555                            'firstpage' => new external_value(PARAM_BOOL, 'True if it\'s the first page.'),
556                            'cachedcontent' => new external_value(PARAM_RAW, 'Page contents.', VALUE_OPTIONAL),
557                            'contentformat' => new external_format_value('cachedcontent', VALUE_OPTIONAL),
558                            'contentsize' => new external_value(PARAM_INT, 'Size of page contents in bytes (doesn\'t include'.
559                                                                            ' size of attached files).', VALUE_OPTIONAL),
560                            'tags' => new external_multiple_structure(
561                                \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', VALUE_OPTIONAL
562                            ),
563                        ), 'Pages'
564                    )
565                ),
566                'warnings' => new external_warnings(),
567            )
568        );
569    }
570
571    /**
572     * Describes the parameters for get_page_contents.
573     *
574     * @return external_function_parameters
575     * @since Moodle 3.1
576     */
577    public static function get_page_contents_parameters() {
578        return new external_function_parameters (
579            array(
580                'pageid' => new external_value(PARAM_INT, 'Page ID.')
581            )
582        );
583    }
584
585    /**
586     * Get a page contents.
587     *
588     * @param int $pageid The page ID.
589     * @return array of warnings and page data.
590     * @since Moodle 3.1
591     */
592    public static function get_page_contents($pageid) {
593
594        $params = self::validate_parameters(self::get_page_contents_parameters(),
595                                            array(
596                                                'pageid' => $pageid
597                                            )
598            );
599        $warnings = array();
600
601        // Get wiki page.
602        if (!$page = wiki_get_page($params['pageid'])) {
603            throw new moodle_exception('incorrectpageid', 'wiki');
604        }
605
606        // Get wiki instance.
607        if (!$wiki = wiki_get_wiki_from_pageid($params['pageid'])) {
608            throw new moodle_exception('incorrectwikiid', 'wiki');
609        }
610
611        // Permission validation.
612        $cm = get_coursemodule_from_instance('wiki', $wiki->id, $wiki->course);
613        $context = context_module::instance($cm->id);
614        self::validate_context($context);
615
616        // Check if user can view this wiki.
617        if (!$subwiki = wiki_get_subwiki($page->subwikiid)) {
618            throw new moodle_exception('incorrectsubwikiid', 'wiki');
619        }
620        if (!wiki_user_can_view($subwiki, $wiki)) {
621            throw new moodle_exception('cannotviewpage', 'wiki');
622        }
623
624        $returnedpage = array();
625        $returnedpage['id'] = $page->id;
626        $returnedpage['wikiid'] = $wiki->id;
627        $returnedpage['subwikiid'] = $page->subwikiid;
628        $returnedpage['groupid'] = $subwiki->groupid;
629        $returnedpage['userid'] = $subwiki->userid;
630        $returnedpage['title'] = $page->title;
631        $returnedpage['tags'] = \core_tag\external\util::get_item_tags('mod_wiki', 'wiki_pages', $page->id);
632
633        // Refresh page cached content if needed.
634        if ($page->timerendered + WIKI_REFRESH_CACHE_TIME < time()) {
635            if ($content = wiki_refresh_cachedcontent($page)) {
636                $page = $content['page'];
637            }
638        }
639
640        list($returnedpage['cachedcontent'], $returnedpage['contentformat']) = external_format_text(
641                            $page->cachedcontent, FORMAT_HTML, $context->id, 'mod_wiki', 'attachments', $subwiki->id);
642        $returnedpage['caneditpage'] = wiki_user_can_edit($subwiki);
643
644        // Get page version.
645        $version = wiki_get_current_version($page->id);
646        if (!empty($version)) {
647            $returnedpage['version'] = $version->version;
648        }
649
650        $result = array();
651        $result['page'] = $returnedpage;
652        $result['warnings'] = $warnings;
653        return $result;
654    }
655
656    /**
657     * Describes the get_page_contents return value.
658     *
659     * @return external_single_structure
660     * @since Moodle 3.1
661     */
662    public static function get_page_contents_returns() {
663        return new external_single_structure(
664            array(
665                'page' => new external_single_structure(
666                    array(
667                        'id' => new external_value(PARAM_INT, 'Page ID.'),
668                        'wikiid' => new external_value(PARAM_INT, 'Page\'s wiki ID.'),
669                        'subwikiid' => new external_value(PARAM_INT, 'Page\'s subwiki ID.'),
670                        'groupid' => new external_value(PARAM_INT, 'Page\'s group ID.'),
671                        'userid' => new external_value(PARAM_INT, 'Page\'s user ID.'),
672                        'title' => new external_value(PARAM_RAW, 'Page title.'),
673                        'cachedcontent' => new external_value(PARAM_RAW, 'Page contents.'),
674                        'contentformat' => new external_format_value('cachedcontent', VALUE_OPTIONAL),
675                        'caneditpage' => new external_value(PARAM_BOOL, 'True if user can edit the page.'),
676                        'version' => new external_value(PARAM_INT, 'Latest version of the page.', VALUE_OPTIONAL),
677                        'tags' => new external_multiple_structure(
678                            \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', VALUE_OPTIONAL
679                        ),
680                    ), 'Page'
681                ),
682                'warnings' => new external_warnings()
683            )
684        );
685    }
686
687    /**
688     * Describes the parameters for get_subwiki_files.
689     *
690     * @return external_function_parameters
691     * @since Moodle 3.1
692     */
693    public static function get_subwiki_files_parameters() {
694        return new external_function_parameters (
695            array(
696                'wikiid' => new external_value(PARAM_INT, 'Wiki instance ID.'),
697                'groupid' => new external_value(PARAM_INT, 'Subwiki\'s group ID, -1 means current group. It will be ignored'
698                                        . ' if the wiki doesn\'t use groups.', VALUE_DEFAULT, -1),
699                'userid' => new external_value(PARAM_INT, 'Subwiki\'s user ID, 0 means current user. It will be ignored'
700                                        .' in collaborative wikis.', VALUE_DEFAULT, 0)
701            )
702        );
703    }
704
705    /**
706     * Returns the list of files from a specific subwiki.
707     *
708     * @param int $wikiid The wiki instance ID.
709     * @param int $groupid The group ID. If not defined, use current group.
710     * @param int $userid The user ID. If not defined, use current user.
711     * @return array Containing a list of warnings and a list of files.
712     * @since Moodle 3.1
713     * @throws moodle_exception
714     */
715    public static function get_subwiki_files($wikiid, $groupid = -1, $userid = 0) {
716
717        $returnedfiles = array();
718        $warnings = array();
719
720        $params = self::validate_parameters(self::get_subwiki_files_parameters(),
721                                            array(
722                                                'wikiid' => $wikiid,
723                                                'groupid' => $groupid,
724                                                'userid' => $userid
725                                                )
726            );
727
728        // Get wiki instance.
729        if (!$wiki = wiki_get_wiki($params['wikiid'])) {
730            throw new moodle_exception('incorrectwikiid', 'wiki');
731        }
732        list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
733        $context = context_module::instance($cm->id);
734        self::validate_context($context);
735
736        // Determine groupid and userid to use.
737        list($groupid, $userid) = self::determine_group_and_user($cm, $wiki, $params['groupid'], $params['userid']);
738
739        // Get subwiki and validate it.
740        $subwiki = wiki_get_subwiki_by_group_and_user_with_validation($wiki, $groupid, $userid);
741
742        // Get subwiki based on group and user.
743        if ($subwiki === false) {
744            throw new moodle_exception('cannotviewfiles', 'wiki');
745        } else if ($subwiki->id != -1) {
746            // The subwiki exists, let's get the files.
747            $returnedfiles = external_util::get_area_files($context->id, 'mod_wiki', 'attachments', $subwiki->id);
748        }
749
750        $result = array();
751        $result['files'] = $returnedfiles;
752        $result['warnings'] = $warnings;
753        return $result;
754    }
755
756    /**
757     * Describes the get_subwiki_pages return value.
758     *
759     * @return external_single_structure
760     * @since Moodle 3.1
761     */
762    public static function get_subwiki_files_returns() {
763
764        return new external_single_structure(
765            array(
766                'files' => new external_files('Files'),
767                'warnings' => new external_warnings(),
768            )
769        );
770    }
771
772    /**
773     * Utility function for determining the groupid and userid to use.
774     *
775     * @param stdClass $cm The course module.
776     * @param stdClass $wiki The wiki.
777     * @param int $groupid Group ID. If not defined, use current group.
778     * @param int $userid User ID. If not defined, use current user.
779     * @return array Array containing the courseid and userid.
780     * @since  Moodle 3.1
781     */
782    protected static function determine_group_and_user($cm, $wiki, $groupid = -1, $userid = 0) {
783        global $USER;
784
785        $currentgroup = groups_get_activity_group($cm);
786        if ($currentgroup === false) {
787            // Activity doesn't use groups.
788            $groupid = 0;
789        } else if ($groupid == -1) {
790            // Use current group.
791            $groupid = !empty($currentgroup) ? $currentgroup : 0;
792        }
793
794        // Determine user.
795        if ($wiki->wikimode == 'collaborative') {
796            // Collaborative wikis don't use userid in subwikis.
797            $userid = 0;
798        } else if (empty($userid)) {
799            // Use current user.
800            $userid = $USER->id;
801        }
802
803        return array($groupid, $userid);
804    }
805
806    /**
807     * Describes the parameters for get_page_for_editing.
808     *
809     * @return external_function_parameters
810     * @since Moodle 3.1
811     */
812    public static function get_page_for_editing_parameters() {
813        return new external_function_parameters (
814            array(
815                'pageid' => new external_value(PARAM_INT, 'Page ID to edit.'),
816                'section' => new external_value(PARAM_RAW, 'Section page title.', VALUE_DEFAULT, null),
817                'lockonly' => new external_value(PARAM_BOOL, 'Just renew lock and not return content.', VALUE_DEFAULT, false)
818            )
819        );
820    }
821
822    /**
823     * Locks and retrieves info of page-section to be edited.
824     *
825     * @param int $pageid The page ID.
826     * @param string $section Section page title.
827     * @param boolean $lockonly If true: Just renew lock and not return content.
828     * @return array of warnings and page data.
829     * @since Moodle 3.1
830     */
831    public static function get_page_for_editing($pageid, $section = null, $lockonly = false) {
832        global $USER;
833
834        $params = self::validate_parameters(self::get_page_for_editing_parameters(),
835                                            array(
836                                                'pageid' => $pageid,
837                                                'section' => $section,
838                                                'lockonly' => $lockonly
839                                            )
840            );
841
842        $warnings = array();
843
844        // Get wiki page.
845        if (!$page = wiki_get_page($params['pageid'])) {
846            throw new moodle_exception('incorrectpageid', 'wiki');
847        }
848
849        // Get wiki instance.
850        if (!$wiki = wiki_get_wiki_from_pageid($params['pageid'])) {
851            throw new moodle_exception('incorrectwikiid', 'wiki');
852        }
853
854        // Get subwiki instance.
855        if (!$subwiki = wiki_get_subwiki($page->subwikiid)) {
856            throw new moodle_exception('incorrectsubwikiid', 'wiki');
857        }
858
859        // Permission validation.
860        $cm = get_coursemodule_from_instance('wiki', $wiki->id, $wiki->course);
861        $context = context_module::instance($cm->id);
862        self::validate_context($context);
863
864        if (!wiki_user_can_edit($subwiki)) {
865            throw new moodle_exception('cannoteditpage', 'wiki');
866        }
867
868        if (!wiki_set_lock($params['pageid'], $USER->id, $params['section'], true)) {
869            throw new moodle_exception('pageislocked', 'wiki');
870        }
871
872        $version = wiki_get_current_version($page->id);
873        if (empty($version)) {
874            throw new moodle_exception('versionerror', 'wiki');
875        }
876
877        $pagesection = array();
878        $pagesection['version'] = $version->version;
879
880        // Content requested to be returned.
881        if (!$lockonly) {
882            if (!is_null($params['section'])) {
883                $content = wiki_parser_proxy::get_section($version->content, $version->contentformat, $params['section']);
884            } else {
885                $content = $version->content;
886            }
887
888            $pagesection['content'] = $content;
889            $pagesection['contentformat'] = $version->contentformat;
890        }
891
892        $result = array();
893        $result['pagesection'] = $pagesection;
894        $result['warnings'] = $warnings;
895        return $result;
896
897    }
898
899    /**
900     * Describes the get_page_for_editing return value.
901     *
902     * @return external_single_structure
903     * @since Moodle 3.1
904     */
905    public static function get_page_for_editing_returns() {
906        return new external_single_structure(
907            array(
908                'pagesection' => new external_single_structure(
909                    array(
910                        'content' => new external_value(PARAM_RAW, 'The contents of the page-section to be edited.',
911                            VALUE_OPTIONAL),
912                        'contentformat' => new external_value(PARAM_TEXT, 'Format of the original content of the page.',
913                            VALUE_OPTIONAL),
914                        'version' => new external_value(PARAM_INT, 'Latest version of the page.'),
915                        'warnings' => new external_warnings()
916                    )
917                )
918            )
919        );
920    }
921
922    /**
923     * Describes the parameters for new_page.
924     *
925     * @return external_function_parameters
926     * @since Moodle 3.1
927     */
928    public static function new_page_parameters() {
929        return new external_function_parameters (
930            array(
931                'title' => new external_value(PARAM_TEXT, 'New page title.'),
932                'content' => new external_value(PARAM_RAW, 'Page contents.'),
933                'contentformat' => new external_value(PARAM_TEXT, 'Page contents format. If an invalid format is provided, default
934                    wiki format is used.', VALUE_DEFAULT, null),
935                'subwikiid' => new external_value(PARAM_INT, 'Page\'s subwiki ID.', VALUE_DEFAULT, null),
936                'wikiid' => new external_value(PARAM_INT, 'Page\'s wiki ID. Used if subwiki does not exists.', VALUE_DEFAULT,
937                    null),
938                'userid' => new external_value(PARAM_INT, 'Subwiki\'s user ID. Used if subwiki does not exists.', VALUE_DEFAULT,
939                    null),
940                'groupid' => new external_value(PARAM_INT, 'Subwiki\'s group ID. Used if subwiki does not exists.', VALUE_DEFAULT,
941                    null)
942            )
943        );
944    }
945
946    /**
947     * Creates a new page.
948     *
949     * @param string $title New page title.
950     * @param string $content Page contents.
951     * @param int $contentformat Page contents format. If an invalid format is provided, default wiki format is used.
952     * @param int $subwikiid The Subwiki ID where to store the page.
953     * @param int $wikiid Page\'s wiki ID. Used if subwiki does not exists.
954     * @param int $userid Subwiki\'s user ID. Used if subwiki does not exists.
955     * @param int $groupid Subwiki\'s group ID. Used if subwiki does not exists.
956     * @return array of warnings and page data.
957     * @since Moodle 3.1
958     */
959    public static function new_page($title, $content, $contentformat = null, $subwikiid = null, $wikiid = null, $userid = null,
960        $groupid = null) {
961        global $USER;
962
963        $params = self::validate_parameters(self::new_page_parameters(),
964                                            array(
965                                                'title' => $title,
966                                                'content' => $content,
967                                                'contentformat' => $contentformat,
968                                                'subwikiid' => $subwikiid,
969                                                'wikiid' => $wikiid,
970                                                'userid' => $userid,
971                                                'groupid' => $groupid
972                                            )
973            );
974
975        $warnings = array();
976
977        // Get wiki and subwiki instances.
978        if (!empty($params['subwikiid'])) {
979            if (!$subwiki = wiki_get_subwiki($params['subwikiid'])) {
980                throw new moodle_exception('incorrectsubwikiid', 'wiki');
981            }
982
983            if (!$wiki = wiki_get_wiki($subwiki->wikiid)) {
984                throw new moodle_exception('incorrectwikiid', 'wiki');
985            }
986
987            // Permission validation.
988            $cm = get_coursemodule_from_instance('wiki', $wiki->id, $wiki->course);
989            $context = context_module::instance($cm->id);
990            self::validate_context($context);
991
992        } else {
993            if (!$wiki = wiki_get_wiki($params['wikiid'])) {
994                throw new moodle_exception('incorrectwikiid', 'wiki');
995            }
996
997            // Permission validation.
998            $cm = get_coursemodule_from_instance('wiki', $wiki->id, $wiki->course);
999            $context = context_module::instance($cm->id);
1000            self::validate_context($context);
1001
1002            // Determine groupid and userid to use.
1003            list($groupid, $userid) = self::determine_group_and_user($cm, $wiki, $params['groupid'], $params['userid']);
1004
1005            // Get subwiki and validate it.
1006            $subwiki = wiki_get_subwiki_by_group_and_user_with_validation($wiki, $groupid, $userid);
1007
1008            if ($subwiki === false) {
1009                // User cannot view page.
1010                throw new moodle_exception('cannoteditpage', 'wiki');
1011            } else if ($subwiki->id < 0) {
1012                // Subwiki needed to check edit permissions.
1013                if (!wiki_user_can_edit($subwiki)) {
1014                    throw new moodle_exception('cannoteditpage', 'wiki');
1015                }
1016
1017                // Subwiki does not exists and it can be created.
1018                $swid = wiki_add_subwiki($wiki->id, $groupid, $userid);
1019                if (!$subwiki = wiki_get_subwiki($swid)) {
1020                    throw new moodle_exception('incorrectsubwikiid', 'wiki');
1021                }
1022            }
1023        }
1024
1025        // Subwiki needed to check edit permissions.
1026        if (!wiki_user_can_edit($subwiki)) {
1027            throw new moodle_exception('cannoteditpage', 'wiki');
1028        }
1029
1030        if ($page = wiki_get_page_by_title($subwiki->id, $params['title'])) {
1031            throw new moodle_exception('pageexists', 'wiki');
1032        }
1033
1034        // Ignore invalid formats and use default instead.
1035        if (!$params['contentformat'] || $wiki->forceformat) {
1036            $params['contentformat'] = $wiki->defaultformat;
1037        } else {
1038            $formats = wiki_get_formats();
1039            if (!in_array($params['contentformat'], $formats)) {
1040                $params['contentformat'] = $wiki->defaultformat;
1041            }
1042        }
1043
1044        $newpageid = wiki_create_page($subwiki->id, $params['title'], $params['contentformat'], $USER->id);
1045
1046        if (!$page = wiki_get_page($newpageid)) {
1047            throw new moodle_exception('incorrectpageid', 'wiki');
1048        }
1049
1050        // Save content.
1051        $save = wiki_save_page($page, $params['content'], $USER->id);
1052
1053        if (!$save) {
1054            throw new moodle_exception('savingerror', 'wiki');
1055        }
1056
1057        $result = array();
1058        $result['pageid'] = $page->id;
1059        $result['warnings'] = $warnings;
1060        return $result;
1061    }
1062
1063    /**
1064     * Describes the new_page return value.
1065     *
1066     * @return external_single_structure
1067     * @since Moodle 3.1
1068     */
1069    public static function new_page_returns() {
1070        return new external_single_structure(
1071            array(
1072                'pageid' => new external_value(PARAM_INT, 'New page id.'),
1073                'warnings' => new external_warnings()
1074            )
1075        );
1076    }
1077
1078    /**
1079     * Describes the parameters for edit_page.
1080     *
1081     * @return external_function_parameters
1082     * @since Moodle 3.1
1083     */
1084    public static function edit_page_parameters() {
1085        return new external_function_parameters (
1086            array(
1087                'pageid' => new external_value(PARAM_INT, 'Page ID.'),
1088                'content' => new external_value(PARAM_RAW, 'Page contents.'),
1089                'section' => new external_value(PARAM_RAW, 'Section page title.', VALUE_DEFAULT, null)
1090            )
1091        );
1092    }
1093
1094    /**
1095     * Edit a page contents.
1096     *
1097     * @param int $pageid The page ID.
1098     * @param string $content Page contents.
1099     * @param int $section Section to be edited.
1100     * @return array of warnings and page data.
1101     * @since Moodle 3.1
1102     */
1103    public static function edit_page($pageid, $content, $section = null) {
1104        global $USER;
1105
1106        $params = self::validate_parameters(self::edit_page_parameters(),
1107                                            array(
1108                                                'pageid' => $pageid,
1109                                                'content' => $content,
1110                                                'section' => $section
1111                                            )
1112            );
1113        $warnings = array();
1114
1115        // Get wiki page.
1116        if (!$page = wiki_get_page($params['pageid'])) {
1117            throw new moodle_exception('incorrectpageid', 'wiki');
1118        }
1119
1120        // Get wiki instance.
1121        if (!$wiki = wiki_get_wiki_from_pageid($params['pageid'])) {
1122            throw new moodle_exception('incorrectwikiid', 'wiki');
1123        }
1124
1125        // Get subwiki instance.
1126        if (!$subwiki = wiki_get_subwiki($page->subwikiid)) {
1127            throw new moodle_exception('incorrectsubwikiid', 'wiki');
1128        }
1129
1130        // Permission validation.
1131        $cm = get_coursemodule_from_instance('wiki', $wiki->id, $wiki->course);
1132        $context = context_module::instance($cm->id);
1133        self::validate_context($context);
1134
1135        if (!wiki_user_can_edit($subwiki)) {
1136            throw new moodle_exception('cannoteditpage', 'wiki');
1137        }
1138
1139        if (wiki_is_page_section_locked($page->id, $USER->id, $params['section'])) {
1140            throw new moodle_exception('pageislocked', 'wiki');
1141        }
1142
1143        // Save content.
1144        if (!is_null($params['section'])) {
1145            $version = wiki_get_current_version($page->id);
1146            $content = wiki_parser_proxy::get_section($version->content, $version->contentformat, $params['section'], false);
1147            if (!$content) {
1148                throw new moodle_exception('invalidsection', 'wiki');
1149            }
1150
1151            $save = wiki_save_section($page, $params['section'], $params['content'], $USER->id);
1152        } else {
1153            $save = wiki_save_page($page, $params['content'], $USER->id);
1154        }
1155
1156        wiki_delete_locks($page->id, $USER->id, $params['section']);
1157
1158        if (!$save) {
1159            throw new moodle_exception('savingerror', 'wiki');
1160        }
1161
1162        $result = array();
1163        $result['pageid'] = $page->id;
1164        $result['warnings'] = $warnings;
1165        return $result;
1166    }
1167
1168    /**
1169     * Describes the edit_page return value.
1170     *
1171     * @return external_single_structure
1172     * @since Moodle 3.1
1173     */
1174    public static function edit_page_returns() {
1175        return new external_single_structure(
1176            array(
1177                'pageid' => new external_value(PARAM_INT, 'Edited page id.'),
1178                'warnings' => new external_warnings()
1179            )
1180        );
1181    }
1182
1183}
1184