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

/**
 * Wiki module external API.
 *
 * @package    mod_wiki
 * @category   external
 * @copyright  2015 Dani Palou <dani@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 * @since      Moodle 3.1
 */

use core_course\external\helper_for_get_mods_by_courses;
use core_external\external_api;
use core_external\external_files;
use core_external\external_format_value;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use core_external\util;

defined('MOODLE_INTERNAL') || die;

require_once($CFG->dirroot . '/mod/wiki/lib.php');
require_once($CFG->dirroot . '/mod/wiki/locallib.php');

/**
 * Wiki module external functions.
 *
 * @package    mod_wiki
 * @category   external
 * @copyright  2015 Dani Palou <dani@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 * @since      Moodle 3.1
 */
class mod_wiki_external extends external_api {

    /**
     * Describes the parameters for get_wikis_by_courses.
     *
     * @return external_function_parameters
     * @since Moodle 3.1
     */
    public static function get_wikis_by_courses_parameters() {
        return new external_function_parameters (
            array(
                'courseids' => new external_multiple_structure(
                    new external_value(PARAM_INT, 'Course ID'), 'Array of course ids.', VALUE_DEFAULT, array()
                ),
            )
        );
    }

    /**
     * Returns a list of wikis in a provided list of courses,
     * if no list is provided all wikis that the user can view will be returned.
     *
     * @param array $courseids The courses IDs.
     * @return array Containing a list of warnings and a list of wikis.
     * @since Moodle 3.1
     */
    public static function get_wikis_by_courses($courseids = array()) {

        $returnedwikis = array();
        $warnings = array();

        $params = self::validate_parameters(self::get_wikis_by_courses_parameters(), array('courseids' => $courseids));

        $mycourses = array();
        if (empty($params['courseids'])) {
            $mycourses = enrol_get_my_courses();
            $params['courseids'] = array_keys($mycourses);
        }

        // Ensure there are courseids to loop through.
        if (!empty($params['courseids'])) {

            list($courses, $warnings) = util::validate_courses($params['courseids'], $mycourses);

            // Get the wikis in this course, this function checks users visibility permissions.
            // We can avoid then additional validate_context calls.
            $wikis = get_all_instances_in_courses('wiki', $courses);

            foreach ($wikis as $wiki) {

                $context = context_module::instance($wiki->coursemodule);

                // Entry to return.
                $module = helper_for_get_mods_by_courses::standard_coursemodule_element_values(
                        $wiki, 'mod_wiki', 'mod/wiki:viewpage', 'mod/wiki:viewpage');

                $viewablefields = [];
                if (has_capability('mod/wiki:viewpage', $context)) {
                    $viewablefields = array('firstpagetitle', 'wikimode', 'defaultformat', 'forceformat', 'editbegin', 'editend',
                                            'section', 'visible', 'groupmode', 'groupingid');
                }

                // Check additional permissions for returning optional private settings.
                if (has_capability('moodle/course:manageactivities', $context)) {
                    $additionalfields = array('timecreated', 'timemodified');
                    $viewablefields = array_merge($viewablefields, $additionalfields);
                }

                foreach ($viewablefields as $field) {
                    $module[$field] = $wiki->{$field};
                }

                // Check if user can add new pages.
                $module['cancreatepages'] = wiki_can_create_pages($context);

                $returnedwikis[] = $module;
            }
        }

        $result = array();
        $result['wikis'] = $returnedwikis;
        $result['warnings'] = $warnings;
        return $result;
    }

    /**
     * Describes the get_wikis_by_courses return value.
     *
     * @return external_single_structure
     * @since Moodle 3.1
     */
    public static function get_wikis_by_courses_returns() {

        return new external_single_structure(
            array(
                'wikis' => new external_multiple_structure(
                    new external_single_structure(array_merge(
                        helper_for_get_mods_by_courses::standard_coursemodule_elements_returns(true),
                        [
                            'timecreated' => new external_value(PARAM_INT, 'Time of creation.', VALUE_OPTIONAL),
                            'timemodified' => new external_value(PARAM_INT, 'Time of last modification.', VALUE_OPTIONAL),
                            'firstpagetitle' => new external_value(PARAM_RAW, 'First page title.', VALUE_OPTIONAL),
                            'wikimode' => new external_value(PARAM_TEXT, 'Wiki mode (individual, collaborative).', VALUE_OPTIONAL),
                            'defaultformat' => new external_value(PARAM_TEXT, 'Wiki\'s default format (html, creole, nwiki).',
                                                                            VALUE_OPTIONAL),
                            'forceformat' => new external_value(PARAM_INT, '1 if format is forced, 0 otherwise.',
                                                                            VALUE_OPTIONAL),
                            'editbegin' => new external_value(PARAM_INT, 'Edit begin.', VALUE_OPTIONAL),
                            'editend' => new external_value(PARAM_INT, 'Edit end.', VALUE_OPTIONAL),
                            'cancreatepages' => new external_value(PARAM_BOOL, 'True if user can create pages.'),
                        ]
                    ), 'Wikis')
                ),
                'warnings' => new external_warnings(),
            )
        );
    }

    /**
     * Describes the parameters for view_wiki.
     *
     * @return external_function_parameters
     * @since Moodle 3.1
     */
    public static function view_wiki_parameters() {
        return new external_function_parameters (
            array(
                'wikiid' => new external_value(PARAM_INT, 'Wiki instance ID.')
            )
        );
    }

    /**
     * Trigger the course module viewed event and update the module completion status.
     *
     * @param int $wikiid The wiki instance ID.
     * @return array of warnings and status result.
     * @since Moodle 3.1
     */
    public static function view_wiki($wikiid) {

        $params = self::validate_parameters(self::view_wiki_parameters(),
                                            array(
                                                'wikiid' => $wikiid
                                            ));
        $warnings = array();

        // Get wiki instance.
        if (!$wiki = wiki_get_wiki($params['wikiid'])) {
            throw new moodle_exception('incorrectwikiid', 'wiki');
        }

        // Permission validation.
        list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
        $context = context_module::instance($cm->id);
        self::validate_context($context);

        // Check if user can view this wiki.
        // We don't use wiki_user_can_view because it requires to have a valid subwiki for the user.
        if (!has_capability('mod/wiki:viewpage', $context)) {
            throw new moodle_exception('cannotviewpage', 'wiki');
        }

        // Trigger course_module_viewed event and completion.
        wiki_view($wiki, $course, $cm, $context);

        $result = array();
        $result['status'] = true;
        $result['warnings'] = $warnings;
        return $result;
    }

    /**
     * Describes the view_wiki return value.
     *
     * @return external_single_structure
     * @since Moodle 3.1
     */
    public static function view_wiki_returns() {
        return new external_single_structure(
            array(
                'status' => new external_value(PARAM_BOOL, 'Status: true if success.'),
                'warnings' => new external_warnings()
            )
        );
    }

    /**
     * Describes the parameters for view_page.
     *
     * @return external_function_parameters
     * @since Moodle 3.1
     */
    public static function view_page_parameters() {
        return new external_function_parameters (
            array(
                'pageid' => new external_value(PARAM_INT, 'Wiki page ID.'),
            )
        );
    }

    /**
     * Trigger the page viewed event and update the module completion status.
     *
     * @param int $pageid The page ID.
     * @return array of warnings and status result.
     * @since Moodle 3.1
     * @throws moodle_exception if page is not valid.
     */
    public static function view_page($pageid) {

        $params = self::validate_parameters(self::view_page_parameters(),
                                            array(
                                                'pageid' => $pageid
                                            ));
        $warnings = array();

        // Get wiki page.
        if (!$page = wiki_get_page($params['pageid'])) {
            throw new moodle_exception('incorrectpageid', 'wiki');
        }

        // Get wiki instance.
        if (!$wiki = wiki_get_wiki_from_pageid($params['pageid'])) {
            throw new moodle_exception('incorrectwikiid', 'wiki');
        }

        // Permission validation.
        list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
        $context = context_module::instance($cm->id);
        self::validate_context($context);

        // Check if user can view this wiki.
        if (!$subwiki = wiki_get_subwiki($page->subwikiid)) {
            throw new moodle_exception('incorrectsubwikiid', 'wiki');
        }
        if (!wiki_user_can_view($subwiki, $wiki)) {
            throw new moodle_exception('cannotviewpage', 'wiki');
        }

        // Trigger page_viewed event and completion.
        wiki_page_view($wiki, $page, $course, $cm, $context);

        $result = array();
        $result['status'] = true;
        $result['warnings'] = $warnings;
        return $result;
    }

    /**
     * Describes the view_page return value.
     *
     * @return external_single_structure
     * @since Moodle 3.1
     */
    public static function view_page_returns() {
        return new external_single_structure(
            array(
                'status' => new external_value(PARAM_BOOL, 'Status: true if success.'),
                'warnings' => new external_warnings()
            )
        );
    }

    /**
     * Describes the parameters for get_subwikis.
     *
     * @return external_function_parameters
     * @since Moodle 3.1
     */
    public static function get_subwikis_parameters() {
        return new external_function_parameters (
            array(
                'wikiid' => new external_value(PARAM_INT, 'Wiki instance ID.')
            )
        );
    }

    /**
     * Returns the list of subwikis the user can see in a specific wiki.
     *
     * @param int $wikiid The wiki instance ID.
     * @return array Containing a list of warnings and a list of subwikis.
     * @since Moodle 3.1
     */
    public static function get_subwikis($wikiid) {
        global $USER;

        $warnings = array();

        $params = self::validate_parameters(self::get_subwikis_parameters(), array('wikiid' => $wikiid));

        // Get wiki instance.
        if (!$wiki = wiki_get_wiki($params['wikiid'])) {
            throw new moodle_exception('incorrectwikiid', 'wiki');
        }

        // Validate context and capabilities.
        list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
        $context = context_module::instance($cm->id);
        self::validate_context($context);
        require_capability('mod/wiki:viewpage', $context);

        $returnedsubwikis = wiki_get_visible_subwikis($wiki, $cm, $context);
        foreach ($returnedsubwikis as $subwiki) {
            $subwiki->canedit = wiki_user_can_edit($subwiki);
        }

        $result = array();
        $result['subwikis'] = $returnedsubwikis;
        $result['warnings'] = $warnings;
        return $result;
    }

    /**
     * Describes the get_subwikis return value.
     *
     * @return external_single_structure
     * @since Moodle 3.1
     */
    public static function get_subwikis_returns() {
        return new external_single_structure(
            array(
                'subwikis' => new external_multiple_structure(
                    new external_single_structure(
                        array(
                            'id' => new external_value(PARAM_INT, 'Subwiki ID.'),
                            'wikiid' => new external_value(PARAM_INT, 'Wiki ID.'),
                            'groupid' => new external_value(PARAM_RAW, 'Group ID.'),
                            'userid' => new external_value(PARAM_INT, 'User ID.'),
                            'canedit' => new external_value(PARAM_BOOL, 'True if user can edit the subwiki.'),
                        ), 'Subwikis'
                    )
                ),
                'warnings' => new external_warnings(),
            )
        );
    }

    /**
     * Describes the parameters for get_subwiki_pages.
     *
     * @return external_function_parameters
     * @since Moodle 3.1
     */
    public static function get_subwiki_pages_parameters() {
        return new external_function_parameters (
            array(
                'wikiid' => new external_value(PARAM_INT, 'Wiki instance ID.'),
                'groupid' => new external_value(PARAM_INT, 'Subwiki\'s group ID, -1 means current group. It will be ignored'
                                        . ' if the wiki doesn\'t use groups.', VALUE_DEFAULT, -1),
                'userid' => new external_value(PARAM_INT, 'Subwiki\'s user ID, 0 means current user. It will be ignored'
                                        .' in collaborative wikis.', VALUE_DEFAULT, 0),
                'options' => new external_single_structure(
                            array(
                                    'sortby' => new external_value(PARAM_ALPHA,
                                            'Field to sort by (id, title, ...).', VALUE_DEFAULT, 'title'),
                                    'sortdirection' => new external_value(PARAM_ALPHA,
                                            'Sort direction: ASC or DESC.', VALUE_DEFAULT, 'ASC'),
                                    'includecontent' => new external_value(PARAM_INT,
                                            'Include each page contents or just the contents size.', VALUE_DEFAULT, 1),
                            ), 'Options', VALUE_DEFAULT, array()),
            )
        );
    }

    /**
     * Returns the list of pages from a specific subwiki.
     *
     * @param int $wikiid The wiki instance ID.
     * @param int $groupid The group ID. If not defined, use current group.
     * @param int $userid The user ID. If not defined, use current user.
     * @param array $options Several options like sort by, sort direction, ...
     * @return array Containing a list of warnings and a list of pages.
     * @since Moodle 3.1
     */
    public static function get_subwiki_pages($wikiid, $groupid = -1, $userid = 0, $options = array()) {

        $returnedpages = array();
        $warnings = array();

        $params = self::validate_parameters(self::get_subwiki_pages_parameters(),
                                            array(
                                                'wikiid' => $wikiid,
                                                'groupid' => $groupid,
                                                'userid' => $userid,
                                                'options' => $options
                                                )
            );

        // Get wiki instance.
        if (!$wiki = wiki_get_wiki($params['wikiid'])) {
            throw new moodle_exception('incorrectwikiid', 'wiki');
        }
        list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
        $context = context_module::instance($cm->id);
        self::validate_context($context);

        // Determine groupid and userid to use.
        list($groupid, $userid) = self::determine_group_and_user($cm, $wiki, $params['groupid'], $params['userid']);

        // Get subwiki and validate it.
        $subwiki = wiki_get_subwiki_by_group_and_user_with_validation($wiki, $groupid, $userid);

        if ($subwiki === false) {
            throw new moodle_exception('cannotviewpage', 'wiki');
        } else if ($subwiki->id != -1) {

            $options = $params['options'];

            // Set sort param.
            $sort = get_safe_orderby([
                'id' => 'id',
                'title' => 'title',
                'timecreated' => 'timecreated',
                'timemodified' => 'timemodified',
                'pageviews' => 'pageviews',
                'default' => 'title',
            ], $options['sortby'], $options['sortdirection'], false);

            $pages = wiki_get_page_list($subwiki->id, $sort);
            $caneditpages = wiki_user_can_edit($subwiki);
            $firstpage = wiki_get_first_page($subwiki->id);

            foreach ($pages as $page) {
                $retpage = array(
                        'id' => $page->id,
                        'subwikiid' => $page->subwikiid,
                        'title' => \core_external\util::format_string($page->title, $context),
                        'timecreated' => $page->timecreated,
                        'timemodified' => $page->timemodified,
                        'timerendered' => $page->timerendered,
                        'userid' => $page->userid,
                        'pageviews' => $page->pageviews,
                        'readonly' => $page->readonly,
                        'caneditpage' => $caneditpages,
                        'firstpage' => $page->id == $firstpage->id,
                        'tags' => \core_tag\external\util::get_item_tags('mod_wiki', 'wiki_pages', $page->id),
                    );

                // Refresh page cached content if needed.
                if ($page->timerendered + WIKI_REFRESH_CACHE_TIME < time()) {
                    if ($content = wiki_refresh_cachedcontent($page)) {
                        $page = $content['page'];
                    }
                }
                list($cachedcontent, $contentformat) = \core_external\util::format_text(
                    $page->cachedcontent,
                    FORMAT_HTML,
                    $context,
                    'mod_wiki',
                    'attachments',
                    $subwiki->id
                );

                if ($options['includecontent']) {
                    // Return the page content.
                    $retpage['cachedcontent'] = $cachedcontent;
                    $retpage['contentformat'] = $contentformat;
                } else {
                    // Return the size of the content.
                    $retpage['contentsize'] = \core_text::strlen($cachedcontent);
                }

                $returnedpages[] = $retpage;
            }
        }

        $result = array();
        $result['pages'] = $returnedpages;
        $result['warnings'] = $warnings;
        return $result;
    }

    /**
     * Describes the get_subwiki_pages return value.
     *
     * @return external_single_structure
     * @since Moodle 3.1
     */
    public static function get_subwiki_pages_returns() {

        return new external_single_structure(
            array(
                'pages' => new external_multiple_structure(
                    new external_single_structure(
                        array(
                            'id' => new external_value(PARAM_INT, 'Page ID.'),
                            'subwikiid' => new external_value(PARAM_INT, 'Page\'s subwiki ID.'),
                            'title' => new external_value(PARAM_RAW, 'Page title.'),
                            'timecreated' => new external_value(PARAM_INT, 'Time of creation.'),
                            'timemodified' => new external_value(PARAM_INT, 'Time of last modification.'),
                            'timerendered' => new external_value(PARAM_INT, 'Time of last renderization.'),
                            'userid' => new external_value(PARAM_INT, 'ID of the user that last modified the page.'),
                            'pageviews' => new external_value(PARAM_INT, 'Number of times the page has been viewed.'),
                            'readonly' => new external_value(PARAM_INT, '1 if readonly, 0 otherwise.'),
                            'caneditpage' => new external_value(PARAM_BOOL, 'True if user can edit the page.'),
                            'firstpage' => new external_value(PARAM_BOOL, 'True if it\'s the first page.'),
                            'cachedcontent' => new external_value(PARAM_RAW, 'Page contents.', VALUE_OPTIONAL),
                            'contentformat' => new external_format_value('cachedcontent', VALUE_OPTIONAL),
                            'contentsize' => new external_value(PARAM_INT, 'Size of page contents in bytes (doesn\'t include'.
                                                                            ' size of attached files).', VALUE_OPTIONAL),
                            'tags' => new external_multiple_structure(
                                \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', VALUE_OPTIONAL
                            ),
                        ), 'Pages'
                    )
                ),
                'warnings' => new external_warnings(),
            )
        );
    }

    /**
     * Describes the parameters for get_page_contents.
     *
     * @return external_function_parameters
     * @since Moodle 3.1
     */
    public static function get_page_contents_parameters() {
        return new external_function_parameters (
            array(
                'pageid' => new external_value(PARAM_INT, 'Page ID.')
            )
        );
    }

    /**
     * Get a page contents.
     *
     * @param int $pageid The page ID.
     * @return array of warnings and page data.
     * @since Moodle 3.1
     */
    public static function get_page_contents($pageid) {

        $params = self::validate_parameters(self::get_page_contents_parameters(),
                                            array(
                                                'pageid' => $pageid
                                            )
            );
        $warnings = array();

        // Get wiki page.
        if (!$page = wiki_get_page($params['pageid'])) {
            throw new moodle_exception('incorrectpageid', 'wiki');
        }

        // Get wiki instance.
        if (!$wiki = wiki_get_wiki_from_pageid($params['pageid'])) {
            throw new moodle_exception('incorrectwikiid', 'wiki');
        }

        // Permission validation.
        $cm = get_coursemodule_from_instance('wiki', $wiki->id, $wiki->course);
        $context = context_module::instance($cm->id);
        self::validate_context($context);

        // Check if user can view this wiki.
        if (!$subwiki = wiki_get_subwiki($page->subwikiid)) {
            throw new moodle_exception('incorrectsubwikiid', 'wiki');
        }
        if (!wiki_user_can_view($subwiki, $wiki)) {
            throw new moodle_exception('cannotviewpage', 'wiki');
        }

        $returnedpage = array();
        $returnedpage['id'] = $page->id;
        $returnedpage['wikiid'] = $wiki->id;
        $returnedpage['subwikiid'] = $page->subwikiid;
        $returnedpage['groupid'] = $subwiki->groupid;
        $returnedpage['userid'] = $subwiki->userid;
        $returnedpage['title'] = $page->title;
        $returnedpage['tags'] = \core_tag\external\util::get_item_tags('mod_wiki', 'wiki_pages', $page->id);

        // Refresh page cached content if needed.
        if ($page->timerendered + WIKI_REFRESH_CACHE_TIME < time()) {
            if ($content = wiki_refresh_cachedcontent($page)) {
                $page = $content['page'];
            }
        }

        list($returnedpage['cachedcontent'], $returnedpage['contentformat']) = \core_external\util::format_text(
            $page->cachedcontent,
            FORMAT_HTML,
            $context,
            'mod_wiki',
            'attachments',
            $subwiki->id
        );
        $returnedpage['caneditpage'] = wiki_user_can_edit($subwiki);

        // Get page version.
        $version = wiki_get_current_version($page->id);
        if (!empty($version)) {
            $returnedpage['version'] = $version->version;
        }

        $result = array();
        $result['page'] = $returnedpage;
        $result['warnings'] = $warnings;
        return $result;
    }

    /**
     * Describes the get_page_contents return value.
     *
     * @return external_single_structure
     * @since Moodle 3.1
     */
    public static function get_page_contents_returns() {
        return new external_single_structure(
            array(
                'page' => new external_single_structure(
                    array(
                        'id' => new external_value(PARAM_INT, 'Page ID.'),
                        'wikiid' => new external_value(PARAM_INT, 'Page\'s wiki ID.'),
                        'subwikiid' => new external_value(PARAM_INT, 'Page\'s subwiki ID.'),
                        'groupid' => new external_value(PARAM_INT, 'Page\'s group ID.'),
                        'userid' => new external_value(PARAM_INT, 'Page\'s user ID.'),
                        'title' => new external_value(PARAM_RAW, 'Page title.'),
                        'cachedcontent' => new external_value(PARAM_RAW, 'Page contents.'),
                        'contentformat' => new external_format_value('cachedcontent', VALUE_OPTIONAL),
                        'caneditpage' => new external_value(PARAM_BOOL, 'True if user can edit the page.'),
                        'version' => new external_value(PARAM_INT, 'Latest version of the page.', VALUE_OPTIONAL),
                        'tags' => new external_multiple_structure(
                            \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', VALUE_OPTIONAL
                        ),
                    ), 'Page'
                ),
                'warnings' => new external_warnings()
            )
        );
    }

    /**
     * Describes the parameters for get_subwiki_files.
     *
     * @return external_function_parameters
     * @since Moodle 3.1
     */
    public static function get_subwiki_files_parameters() {
        return new external_function_parameters (
            array(
                'wikiid' => new external_value(PARAM_INT, 'Wiki instance ID.'),
                'groupid' => new external_value(PARAM_INT, 'Subwiki\'s group ID, -1 means current group. It will be ignored'
                                        . ' if the wiki doesn\'t use groups.', VALUE_DEFAULT, -1),
                'userid' => new external_value(PARAM_INT, 'Subwiki\'s user ID, 0 means current user. It will be ignored'
                                        .' in collaborative wikis.', VALUE_DEFAULT, 0)
            )
        );
    }

    /**
     * Returns the list of files from a specific subwiki.
     *
     * @param int $wikiid The wiki instance ID.
     * @param int $groupid The group ID. If not defined, use current group.
     * @param int $userid The user ID. If not defined, use current user.
     * @return array Containing a list of warnings and a list of files.
     * @since Moodle 3.1
     * @throws moodle_exception
     */
    public static function get_subwiki_files($wikiid, $groupid = -1, $userid = 0) {

        $returnedfiles = array();
        $warnings = array();

        $params = self::validate_parameters(self::get_subwiki_files_parameters(),
                                            array(
                                                'wikiid' => $wikiid,
                                                'groupid' => $groupid,
                                                'userid' => $userid
                                                )
            );

        // Get wiki instance.
        if (!$wiki = wiki_get_wiki($params['wikiid'])) {
            throw new moodle_exception('incorrectwikiid', 'wiki');
        }
        list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
        $context = context_module::instance($cm->id);
        self::validate_context($context);

        // Determine groupid and userid to use.
        list($groupid, $userid) = self::determine_group_and_user($cm, $wiki, $params['groupid'], $params['userid']);

        // Get subwiki and validate it.
        $subwiki = wiki_get_subwiki_by_group_and_user_with_validation($wiki, $groupid, $userid);

        // Get subwiki based on group and user.
        if ($subwiki === false) {
            throw new moodle_exception('cannotviewfiles', 'wiki');
        } else if ($subwiki->id != -1) {
            // The subwiki exists, let's get the files.
            $returnedfiles = util::get_area_files($context->id, 'mod_wiki', 'attachments', $subwiki->id);
        }

        $result = array();
        $result['files'] = $returnedfiles;
        $result['warnings'] = $warnings;
        return $result;
    }

    /**
     * Describes the get_subwiki_pages return value.
     *
     * @return external_single_structure
     * @since Moodle 3.1
     */
    public static function get_subwiki_files_returns() {

        return new external_single_structure(
            array(
                'files' => new external_files('Files'),
                'warnings' => new external_warnings(),
            )
        );
    }

    /**
     * Utility function for determining the groupid and userid to use.
     *
     * @param stdClass $cm The course module.
     * @param stdClass $wiki The wiki.
     * @param int $groupid Group ID. If not defined, use current group.
     * @param int $userid User ID. If not defined, use current user.
     * @return array Array containing the courseid and userid.
     * @since  Moodle 3.1
     */
    protected static function determine_group_and_user($cm, $wiki, $groupid = -1, $userid = 0) {
        global $USER;

        $currentgroup = groups_get_activity_group($cm);
        if ($currentgroup === false) {
            // Activity doesn't use groups.
            $groupid = 0;
        } else if ($groupid == -1) {
            // Use current group.
            $groupid = !empty($currentgroup) ? $currentgroup : 0;
        }

        // Determine user.
        if ($wiki->wikimode == 'collaborative') {
            // Collaborative wikis don't use userid in subwikis.
            $userid = 0;
        } else if (empty($userid)) {
            // Use current user.
            $userid = $USER->id;
        }

        return array($groupid, $userid);
    }

    /**
     * Describes the parameters for get_page_for_editing.
     *
     * @return external_function_parameters
     * @since Moodle 3.1
     */
    public static function get_page_for_editing_parameters() {
        return new external_function_parameters (
            array(
                'pageid' => new external_value(PARAM_INT, 'Page ID to edit.'),
                'section' => new external_value(PARAM_RAW, 'Section page title.', VALUE_DEFAULT, null),
                'lockonly' => new external_value(PARAM_BOOL, 'Just renew lock and not return content.', VALUE_DEFAULT, false)
            )
        );
    }

    /**
     * Locks and retrieves info of page-section to be edited.
     *
     * @param int $pageid The page ID.
     * @param string $section Section page title.
     * @param boolean $lockonly If true: Just renew lock and not return content.
     * @return array of warnings and page data.
     * @since Moodle 3.1
     */
    public static function get_page_for_editing($pageid, $section = null, $lockonly = false) {
        global $USER;

        $params = self::validate_parameters(self::get_page_for_editing_parameters(),
                                            array(
                                                'pageid' => $pageid,
                                                'section' => $section,
                                                'lockonly' => $lockonly
                                            )
            );

        $warnings = array();

        // Get wiki page.
        if (!$page = wiki_get_page($params['pageid'])) {
            throw new moodle_exception('incorrectpageid', 'wiki');
        }

        // Get wiki instance.
        if (!$wiki = wiki_get_wiki_from_pageid($params['pageid'])) {
            throw new moodle_exception('incorrectwikiid', 'wiki');
        }

        // Get subwiki instance.
        if (!$subwiki = wiki_get_subwiki($page->subwikiid)) {
            throw new moodle_exception('incorrectsubwikiid', 'wiki');
        }

        // Permission validation.
        $cm = get_coursemodule_from_instance('wiki', $wiki->id, $wiki->course);
        $context = context_module::instance($cm->id);
        self::validate_context($context);

        if (!wiki_user_can_edit($subwiki)) {
            throw new moodle_exception('cannoteditpage', 'wiki');
        }

        if (!wiki_set_lock($params['pageid'], $USER->id, $params['section'], true)) {
            throw new moodle_exception('pageislocked', 'wiki');
        }

        $version = wiki_get_current_version($page->id);
        if (empty($version)) {
            throw new moodle_exception('versionerror', 'wiki');
        }

        $pagesection = array();
        $pagesection['version'] = $version->version;

        // Content requested to be returned.
        if (!$lockonly) {
            if (!is_null($params['section'])) {
                $content = wiki_parser_proxy::get_section($version->content, $version->contentformat, $params['section']);
            } else {
                $content = $version->content;
            }

            $pagesection['content'] = $content;
            $pagesection['contentformat'] = $version->contentformat;
        }

        $result = array();
        $result['pagesection'] = $pagesection;
        $result['warnings'] = $warnings;
        return $result;

    }

    /**
     * Describes the get_page_for_editing return value.
     *
     * @return external_single_structure
     * @since Moodle 3.1
     */
    public static function get_page_for_editing_returns() {
        return new external_single_structure(
            array(
                'pagesection' => new external_single_structure(
                    array(
                        'content' => new external_value(PARAM_RAW, 'The contents of the page-section to be edited.',
                            VALUE_OPTIONAL),
                        'contentformat' => new external_value(PARAM_TEXT, 'Format of the original content of the page.',
                            VALUE_OPTIONAL),
                        'version' => new external_value(PARAM_INT, 'Latest version of the page.'),
                        'warnings' => new external_warnings()
                    )
                )
            )
        );
    }

    /**
     * Describes the parameters for new_page.
     *
     * @return external_function_parameters
     * @since Moodle 3.1
     */
    public static function new_page_parameters() {
        return new external_function_parameters (
            array(
                'title' => new external_value(PARAM_TEXT, 'New page title.'),
                'content' => new external_value(PARAM_RAW, 'Page contents.'),
                'contentformat' => new external_value(PARAM_TEXT, 'Page contents format. If an invalid format is provided, default
                    wiki format is used.', VALUE_DEFAULT, null),
                'subwikiid' => new external_value(PARAM_INT, 'Page\'s subwiki ID.', VALUE_DEFAULT, null),
                'wikiid' => new external_value(PARAM_INT, 'Page\'s wiki ID. Used if subwiki does not exists.', VALUE_DEFAULT,
                    null),
                'userid' => new external_value(PARAM_INT, 'Subwiki\'s user ID. Used if subwiki does not exists.', VALUE_DEFAULT,
                    null),
                'groupid' => new external_value(PARAM_INT, 'Subwiki\'s group ID. Used if subwiki does not exists.', VALUE_DEFAULT,
                    null)
            )
        );
    }

    /**
     * Creates a new page.
     *
     * @param string $title New page title.
     * @param string $content Page contents.
     * @param int $contentformat Page contents format. If an invalid format is provided, default wiki format is used.
     * @param int $subwikiid The Subwiki ID where to store the page.
     * @param int $wikiid Page\'s wiki ID. Used if subwiki does not exists.
     * @param int $userid Subwiki\'s user ID. Used if subwiki does not exists.
     * @param int $groupid Subwiki\'s group ID. Used if subwiki does not exists.
     * @return array of warnings and page data.
     * @since Moodle 3.1
     */
    public static function new_page($title, $content, $contentformat = null, $subwikiid = null, $wikiid = null, $userid = null,
        $groupid = null) {
        global $USER;

        $params = self::validate_parameters(self::new_page_parameters(),
                                            array(
                                                'title' => $title,
                                                'content' => $content,
                                                'contentformat' => $contentformat,
                                                'subwikiid' => $subwikiid,
                                                'wikiid' => $wikiid,
                                                'userid' => $userid,
                                                'groupid' => $groupid
                                            )
            );

        $warnings = array();

        // Get wiki and subwiki instances.
        if (!empty($params['subwikiid'])) {
            if (!$subwiki = wiki_get_subwiki($params['subwikiid'])) {
                throw new moodle_exception('incorrectsubwikiid', 'wiki');
            }

            if (!$wiki = wiki_get_wiki($subwiki->wikiid)) {
                throw new moodle_exception('incorrectwikiid', 'wiki');
            }

            // Permission validation.
            $cm = get_coursemodule_from_instance('wiki', $wiki->id, $wiki->course);
            $context = context_module::instance($cm->id);
            self::validate_context($context);

        } else {
            if (!$wiki = wiki_get_wiki($params['wikiid'])) {
                throw new moodle_exception('incorrectwikiid', 'wiki');
            }

            // Permission validation.
            $cm = get_coursemodule_from_instance('wiki', $wiki->id, $wiki->course);
            $context = context_module::instance($cm->id);
            self::validate_context($context);

            // Determine groupid and userid to use.
            list($groupid, $userid) = self::determine_group_and_user($cm, $wiki, $params['groupid'], $params['userid']);

            // Get subwiki and validate it.
            $subwiki = wiki_get_subwiki_by_group_and_user_with_validation($wiki, $groupid, $userid);

            if ($subwiki === false) {
                // User cannot view page.
                throw new moodle_exception('cannoteditpage', 'wiki');
            } else if ($subwiki->id < 0) {
                // Subwiki needed to check edit permissions.
                if (!wiki_user_can_edit($subwiki)) {
                    throw new moodle_exception('cannoteditpage', 'wiki');
                }

                // Subwiki does not exists and it can be created.
                $swid = wiki_add_subwiki($wiki->id, $groupid, $userid);
                if (!$subwiki = wiki_get_subwiki($swid)) {
                    throw new moodle_exception('incorrectsubwikiid', 'wiki');
                }
            }
        }

        // Subwiki needed to check edit permissions.
        if (!wiki_user_can_edit($subwiki)) {
            throw new moodle_exception('cannoteditpage', 'wiki');
        }

        if ($page = wiki_get_page_by_title($subwiki->id, $params['title'])) {
            throw new moodle_exception('pageexists', 'wiki');
        }

        // Ignore invalid formats and use default instead.
        if (!$params['contentformat'] || $wiki->forceformat) {
            $params['contentformat'] = $wiki->defaultformat;
        } else {
            $formats = wiki_get_formats();
            if (!in_array($params['contentformat'], $formats)) {
                $params['contentformat'] = $wiki->defaultformat;
            }
        }

        $newpageid = wiki_create_page($subwiki->id, $params['title'], $params['contentformat'], $USER->id);

        if (!$page = wiki_get_page($newpageid)) {
            throw new moodle_exception('incorrectpageid', 'wiki');
        }

        // Save content.
        $save = wiki_save_page($page, $params['content'], $USER->id);

        if (!$save) {
            throw new moodle_exception('savingerror', 'wiki');
        }

        $result = array();
        $result['pageid'] = $page->id;
        $result['warnings'] = $warnings;
        return $result;
    }

    /**
     * Describes the new_page return value.
     *
     * @return external_single_structure
     * @since Moodle 3.1
     */
    public static function new_page_returns() {
        return new external_single_structure(
            array(
                'pageid' => new external_value(PARAM_INT, 'New page id.'),
                'warnings' => new external_warnings()
            )
        );
    }

    /**
     * Describes the parameters for edit_page.
     *
     * @return external_function_parameters
     * @since Moodle 3.1
     */
    public static function edit_page_parameters() {
        return new external_function_parameters (
            array(
                'pageid' => new external_value(PARAM_INT, 'Page ID.'),
                'content' => new external_value(PARAM_RAW, 'Page contents.'),
                'section' => new external_value(PARAM_RAW, 'Section page title.', VALUE_DEFAULT, null)
            )
        );
    }

    /**
     * Edit a page contents.
     *
     * @param int $pageid The page ID.
     * @param string $content Page contents.
     * @param int $section Section to be edited.
     * @return array of warnings and page data.
     * @since Moodle 3.1
     */
    public static function edit_page($pageid, $content, $section = null) {
        global $USER;

        $params = self::validate_parameters(self::edit_page_parameters(),
                                            array(
                                                'pageid' => $pageid,
                                                'content' => $content,
                                                'section' => $section
                                            )
            );
        $warnings = array();

        // Get wiki page.
        if (!$page = wiki_get_page($params['pageid'])) {
            throw new moodle_exception('incorrectpageid', 'wiki');
        }

        // Get wiki instance.
        if (!$wiki = wiki_get_wiki_from_pageid($params['pageid'])) {
            throw new moodle_exception('incorrectwikiid', 'wiki');
        }

        // Get subwiki instance.
        if (!$subwiki = wiki_get_subwiki($page->subwikiid)) {
            throw new moodle_exception('incorrectsubwikiid', 'wiki');
        }

        // Permission validation.
        $cm = get_coursemodule_from_instance('wiki', $wiki->id, $wiki->course);
        $context = context_module::instance($cm->id);
        self::validate_context($context);

        if (!wiki_user_can_edit($subwiki)) {
            throw new moodle_exception('cannoteditpage', 'wiki');
        }

        if (wiki_is_page_section_locked($page->id, $USER->id, $params['section'])) {
            throw new moodle_exception('pageislocked', 'wiki');
        }

        // Save content.
        if (!is_null($params['section'])) {
            $version = wiki_get_current_version($page->id);
            $content = wiki_parser_proxy::get_section($version->content, $version->contentformat, $params['section'], false);
            if (!$content) {
                throw new moodle_exception('invalidsection', 'wiki');
            }

            $save = wiki_save_section($page, $params['section'], $params['content'], $USER->id);
        } else {
            $save = wiki_save_page($page, $params['content'], $USER->id);
        }

        wiki_delete_locks($page->id, $USER->id, $params['section']);

        if (!$save) {
            throw new moodle_exception('savingerror', 'wiki');
        }

        $result = array();
        $result['pageid'] = $page->id;
        $result['warnings'] = $warnings;
        return $result;
    }

    /**
     * Describes the edit_page return value.
     *
     * @return external_single_structure
     * @since Moodle 3.1
     */
    public static function edit_page_returns() {
        return new external_single_structure(
            array(
                'pageid' => new external_value(PARAM_INT, 'Edited page id.'),
                'warnings' => new external_warnings()
            )
        );
    }

}
