2 // This file is part of Moodle - http://moodle.org/
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.
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.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Wiki module external API.
22 * @copyright 2015 Dani Palou <dani@moodle.com>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') || die;
29 require_once($CFG->libdir . '/externallib.php');
30 require_once($CFG->dirroot . '/mod/wiki/lib.php');
31 require_once($CFG->dirroot . '/mod/wiki/locallib.php');
34 * Wiki module external functions.
38 * @copyright 2015 Dani Palou <dani@moodle.com>
39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42 class mod_wiki_external extends external_api {
45 * Describes the parameters for get_wikis_by_courses.
47 * @return external_function_parameters
50 public static function get_wikis_by_courses_parameters() {
51 return new external_function_parameters (
53 'courseids' => new external_multiple_structure(
54 new external_value(PARAM_INT, 'Course ID'), 'Array of course ids.', VALUE_DEFAULT, array()
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.
64 * @param array $courseids The courses IDs.
65 * @return array Containing a list of warnings and a list of wikis.
68 public static function get_wikis_by_courses($courseids = array()) {
70 $returnedwikis = array();
73 $params = self::validate_parameters(self::get_wikis_by_courses_parameters(), array('courseids' => $courseids));
76 if (empty($params['courseids'])) {
77 $mycourses = enrol_get_my_courses();
78 $params['courseids'] = array_keys($mycourses);
81 // Ensure there are courseids to loop through.
82 if (!empty($params['courseids'])) {
84 list($courses, $warnings) = external_util::validate_courses($params['courseids'], $mycourses);
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);
90 foreach ($wikis as $wiki) {
92 $context = context_module::instance($wiki->coursemodule);
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);
103 $viewablefields = [];
104 if (has_capability('mod/wiki:viewpage', $context)) {
105 list($module['intro'], $module['introformat']) =
106 external_format_text($wiki->intro, $wiki->introformat, $context->id, 'mod_wiki', 'intro', $wiki->id);
108 $viewablefields = array('firstpagetitle', 'wikimode', 'defaultformat', 'forceformat', 'editbegin', 'editend',
109 'section', 'visible', 'groupmode', 'groupingid');
112 // Check additional permissions for returning optional private settings.
113 if (has_capability('moodle/course:manageactivities', $context)) {
114 $additionalfields = array('timecreated', 'timemodified');
115 $viewablefields = array_merge($viewablefields, $additionalfields);
118 foreach ($viewablefields as $field) {
119 $module[$field] = $wiki->{$field};
122 // Check if user can add new pages.
123 $module['cancreatepages'] = wiki_can_create_pages($context);
125 $returnedwikis[] = $module;
130 $result['wikis'] = $returnedwikis;
131 $result['warnings'] = $warnings;
136 * Describes the get_wikis_by_courses return value.
138 * @return external_single_structure
141 public static function get_wikis_by_courses_returns() {
143 return new external_single_structure(
145 'wikis' => new external_multiple_structure(
146 new external_single_structure(
148 'id' => new external_value(PARAM_INT, 'Wiki ID.'),
149 'coursemodule' => new external_value(PARAM_INT, 'Course module ID.'),
150 'course' => new external_value(PARAM_INT, 'Course ID.'),
151 'name' => new external_value(PARAM_RAW, 'Wiki name.'),
152 'intro' => new external_value(PARAM_RAW, 'Wiki intro.', VALUE_OPTIONAL),
153 'introformat' => new external_format_value('Wiki intro format.', VALUE_OPTIONAL),
154 'timecreated' => new external_value(PARAM_INT, 'Time of creation.', VALUE_OPTIONAL),
155 'timemodified' => new external_value(PARAM_INT, 'Time of last modification.', VALUE_OPTIONAL),
156 'firstpagetitle' => new external_value(PARAM_RAW, 'First page title.', VALUE_OPTIONAL),
157 'wikimode' => new external_value(PARAM_TEXT, 'Wiki mode (individual, collaborative).', VALUE_OPTIONAL),
158 'defaultformat' => new external_value(PARAM_TEXT, 'Wiki\'s default format (html, creole, nwiki).',
160 'forceformat' => new external_value(PARAM_INT, '1 if format is forced, 0 otherwise.',
162 'editbegin' => new external_value(PARAM_INT, 'Edit begin.', VALUE_OPTIONAL),
163 'editend' => new external_value(PARAM_INT, 'Edit end.', VALUE_OPTIONAL),
164 'section' => new external_value(PARAM_INT, 'Course section ID.', VALUE_OPTIONAL),
165 'visible' => new external_value(PARAM_INT, '1 if visible, 0 otherwise.', VALUE_OPTIONAL),
166 'groupmode' => new external_value(PARAM_INT, 'Group mode.', VALUE_OPTIONAL),
167 'groupingid' => new external_value(PARAM_INT, 'Group ID.', VALUE_OPTIONAL),
168 'cancreatepages' => new external_value(PARAM_BOOL, 'True if user can create pages.'),
172 'warnings' => new external_warnings(),
178 * Describes the parameters for view_wiki.
180 * @return external_function_parameters
183 public static function view_wiki_parameters() {
184 return new external_function_parameters (
186 'wikiid' => new external_value(PARAM_INT, 'Wiki instance ID.')
192 * Trigger the course module viewed event and update the module completion status.
194 * @param int $wikiid The wiki instance ID.
195 * @return array of warnings and status result.
198 public static function view_wiki($wikiid) {
200 $params = self::validate_parameters(self::view_wiki_parameters(),
206 // Get wiki instance.
207 if (!$wiki = wiki_get_wiki($params['wikiid'])) {
208 throw new moodle_exception('incorrectwikiid', 'wiki');
211 // Permission validation.
212 list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
213 $context = context_module::instance($cm->id);
214 self::validate_context($context);
216 // Check if user can view this wiki.
217 // We don't use wiki_user_can_view because it requires to have a valid subwiki for the user.
218 if (!has_capability('mod/wiki:viewpage', $context)) {
219 throw new moodle_exception('cannotviewpage', 'wiki');
222 // Trigger course_module_viewed event and completion.
223 wiki_view($wiki, $course, $cm, $context);
226 $result['status'] = true;
227 $result['warnings'] = $warnings;
232 * Describes the view_wiki return value.
234 * @return external_single_structure
237 public static function view_wiki_returns() {
238 return new external_single_structure(
240 'status' => new external_value(PARAM_BOOL, 'Status: true if success.'),
241 'warnings' => new external_warnings()
247 * Describes the parameters for view_page.
249 * @return external_function_parameters
252 public static function view_page_parameters() {
253 return new external_function_parameters (
255 'pageid' => new external_value(PARAM_INT, 'Wiki page ID.'),
261 * Trigger the page viewed event and update the module completion status.
263 * @param int $pageid The page ID.
264 * @return array of warnings and status result.
266 * @throws moodle_exception if page is not valid.
268 public static function view_page($pageid) {
270 $params = self::validate_parameters(self::view_page_parameters(),
277 if (!$page = wiki_get_page($params['pageid'])) {
278 throw new moodle_exception('incorrectpageid', 'wiki');
281 // Get wiki instance.
282 if (!$wiki = wiki_get_wiki_from_pageid($params['pageid'])) {
283 throw new moodle_exception('incorrectwikiid', 'wiki');
286 // Permission validation.
287 list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
288 $context = context_module::instance($cm->id);
289 self::validate_context($context);
291 // Check if user can view this wiki.
292 if (!$subwiki = wiki_get_subwiki($page->subwikiid)) {
293 throw new moodle_exception('incorrectsubwikiid', 'wiki');
295 if (!wiki_user_can_view($subwiki, $wiki)) {
296 throw new moodle_exception('cannotviewpage', 'wiki');
299 // Trigger page_viewed event and completion.
300 wiki_page_view($wiki, $page, $course, $cm, $context);
303 $result['status'] = true;
304 $result['warnings'] = $warnings;
309 * Describes the view_page return value.
311 * @return external_single_structure
314 public static function view_page_returns() {
315 return new external_single_structure(
317 'status' => new external_value(PARAM_BOOL, 'Status: true if success.'),
318 'warnings' => new external_warnings()
324 * Describes the parameters for get_subwikis.
326 * @return external_function_parameters
329 public static function get_subwikis_parameters() {
330 return new external_function_parameters (
332 'wikiid' => new external_value(PARAM_INT, 'Wiki instance ID.')
338 * Returns the list of subwikis the user can see in a specific wiki.
340 * @param int $wikiid The wiki instance ID.
341 * @return array Containing a list of warnings and a list of subwikis.
344 public static function get_subwikis($wikiid) {
349 $params = self::validate_parameters(self::get_subwikis_parameters(), array('wikiid' => $wikiid));
351 // Get wiki instance.
352 if (!$wiki = wiki_get_wiki($params['wikiid'])) {
353 throw new moodle_exception('incorrectwikiid', 'wiki');
356 // Validate context and capabilities.
357 list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
358 $context = context_module::instance($cm->id);
359 self::validate_context($context);
360 require_capability('mod/wiki:viewpage', $context);
362 $returnedsubwikis = wiki_get_visible_subwikis($wiki, $cm, $context);
363 foreach ($returnedsubwikis as $subwiki) {
364 $subwiki->canedit = wiki_user_can_edit($subwiki);
368 $result['subwikis'] = $returnedsubwikis;
369 $result['warnings'] = $warnings;
374 * Describes the get_subwikis return value.
376 * @return external_single_structure
379 public static function get_subwikis_returns() {
380 return new external_single_structure(
382 'subwikis' => new external_multiple_structure(
383 new external_single_structure(
385 'id' => new external_value(PARAM_INT, 'Subwiki ID.'),
386 'wikiid' => new external_value(PARAM_INT, 'Wiki ID.'),
387 'groupid' => new external_value(PARAM_RAW, 'Group ID.'),
388 'userid' => new external_value(PARAM_INT, 'User ID.'),
389 'canedit' => new external_value(PARAM_BOOL, 'True if user can edit the subwiki.'),
393 'warnings' => new external_warnings(),
399 * Describes the parameters for get_subwiki_pages.
401 * @return external_function_parameters
404 public static function get_subwiki_pages_parameters() {
405 return new external_function_parameters (
407 'wikiid' => new external_value(PARAM_INT, 'Wiki instance ID.'),
408 'groupid' => new external_value(PARAM_INT, 'Subwiki\'s group ID, -1 means current group. It will be ignored'
409 . ' if the wiki doesn\'t use groups.', VALUE_DEFAULT, -1),
410 'userid' => new external_value(PARAM_INT, 'Subwiki\'s user ID, 0 means current user. It will be ignored'
411 .' in collaborative wikis.', VALUE_DEFAULT, 0),
412 'options' => new external_single_structure(
414 'sortby' => new external_value(PARAM_ALPHA,
415 'Field to sort by (id, title, ...).', VALUE_DEFAULT, 'title'),
416 'sortdirection' => new external_value(PARAM_ALPHA,
417 'Sort direction: ASC or DESC.', VALUE_DEFAULT, 'ASC'),
418 'includecontent' => new external_value(PARAM_INT,
419 'Include each page contents or not.', VALUE_DEFAULT, 1),
420 ), 'Options', VALUE_DEFAULT, array()),
426 * Returns the list of pages from a specific subwiki.
428 * @param int $wikiid The wiki instance ID.
429 * @param int $groupid The group ID. If not defined, use current group.
430 * @param int $userid The user ID. If not defined, use current user.
431 * @param array $options Several options like sort by, sort direction, ...
432 * @return array Containing a list of warnings and a list of pages.
435 public static function get_subwiki_pages($wikiid, $groupid = -1, $userid = 0, $options = array()) {
438 $returnedpages = array();
441 $params = self::validate_parameters(self::get_subwiki_pages_parameters(),
444 'groupid' => $groupid,
446 'options' => $options
450 // Get wiki instance.
451 if (!$wiki = wiki_get_wiki($params['wikiid'])) {
452 throw new moodle_exception('incorrectwikiid', 'wiki');
454 list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki');
455 $context = context_module::instance($cm->id);
456 self::validate_context($context);
459 $groupmode = groups_get_activity_groupmode($cm);
460 if ($groupmode == NOGROUPS) {
462 } else if ($params['groupid'] == -1) {
463 // Use current group.
464 $groupid = groups_get_activity_group($cm);
465 $groupid = !empty($groupid) ? $groupid : 0;
467 $groupid = $params['groupid'];
471 if ($wiki->wikimode == 'collaborative') {
472 // Collaborative wikis don't use userid in subwikis.
474 } else if (empty($params['userid'])) {
478 $userid = $params['userid'];
481 // Get subwiki based on group and user.
482 if (!$subwiki = wiki_get_subwiki_by_group($cm->instance, $groupid, $userid)) {
483 // The subwiki doesn't exist.
484 // Validate if user is valid.
485 if ($userid != 0 && $userid != $USER->id && !$user = $DB->get_record('user', array('id' => $userid))) {
486 throw new moodle_exception('invaliduserid', 'error');
489 // Validate that groupid is valid.
490 if ($groupid != 0 && !groups_group_exists($groupid)) {
491 throw new moodle_exception('cannotfindgroup', 'error');
494 // Valid data but subwiki not found. We'll simulate a subwiki object to check if the user would be able to see it
495 // if it existed. If he's able to see it then we'll return an empty array because the subwiki has no pages.
496 $subwiki = new stdClass();
497 $subwiki->wikiid = $wiki->id;
498 $subwiki->userid = $userid;
499 $subwiki->groupid = $groupid;
501 // Check that the user can view the subwiki. This function checks capabilities.
502 if (!wiki_user_can_view($subwiki, $wiki)) {
503 throw new moodle_exception('cannotviewpage', 'wiki');
506 // Check that the user can view the subwiki. This function checks capabilities.
507 if (!wiki_user_can_view($subwiki, $wiki)) {
508 throw new moodle_exception('cannotviewpage', 'wiki');
512 $options = $params['options'];
513 if (!empty($options['sortby'])) {
514 if ($options['sortdirection'] != 'ASC' && $options['sortdirection'] != 'DESC') {
515 // Invalid sort direction. Use default.
516 $options['sortdirection'] = 'ASC';
518 $sort = $options['sortby'] . ' ' . $options['sortdirection'];
521 $pages = wiki_get_page_list($subwiki->id, $sort);
522 $caneditpages = wiki_user_can_edit($subwiki);
523 $firstpage = wiki_get_first_page($subwiki->id);
525 foreach ($pages as $page) {
528 'subwikiid' => $page->subwikiid,
529 'title' => external_format_string($page->title, $context->id),
530 'timecreated' => $page->timecreated,
531 'timemodified' => $page->timemodified,
532 'timerendered' => $page->timerendered,
533 'userid' => $page->userid,
534 'pageviews' => $page->pageviews,
535 'readonly' => $page->readonly,
536 'caneditpage' => $caneditpages,
537 'firstpage' => $page->id == $firstpage->id
540 // Refresh page cached content if needed.
541 if ($page->timerendered + WIKI_REFRESH_CACHE_TIME < time()) {
542 if ($content = wiki_refresh_cachedcontent($page)) {
543 $page = $content['page'];
546 list($cachedcontent, $contentformat) = external_format_text(
547 $page->cachedcontent, FORMAT_HTML, $context->id, 'mod_wiki', 'attachments', $subwiki->id);
549 if ($options['includecontent']) {
550 // Return the page content.
551 $retpage['cachedcontent'] = $cachedcontent;
552 $retpage['contentformat'] = $contentformat;
554 // Return the size of the content.
555 if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
556 $retpage['contentsize'] = mb_strlen($cachedcontent, '8bit');
558 $retpage['contentsize'] = strlen($cachedcontent);
562 $returnedpages[] = $retpage;
568 $result['pages'] = $returnedpages;
569 $result['warnings'] = $warnings;
574 * Describes the get_subwiki_pages return value.
576 * @return external_single_structure
579 public static function get_subwiki_pages_returns() {
581 return new external_single_structure(
583 'pages' => new external_multiple_structure(
584 new external_single_structure(
586 'id' => new external_value(PARAM_INT, 'Page ID.'),
587 'subwikiid' => new external_value(PARAM_INT, 'Page\'s subwiki ID.'),
588 'title' => new external_value(PARAM_RAW, 'Page title.'),
589 'timecreated' => new external_value(PARAM_INT, 'Time of creation.'),
590 'timemodified' => new external_value(PARAM_INT, 'Time of last modification.'),
591 'timerendered' => new external_value(PARAM_INT, 'Time of last renderization.'),
592 'userid' => new external_value(PARAM_INT, 'ID of the user that last modified the page.'),
593 'pageviews' => new external_value(PARAM_INT, 'Number of times the page has been viewed.'),
594 'readonly' => new external_value(PARAM_INT, '1 if readonly, 0 otherwise.'),
595 'caneditpage' => new external_value(PARAM_BOOL, 'True if user can edit the page.'),
596 'firstpage' => new external_value(PARAM_BOOL, 'True if it\'s the first page.'),
597 'cachedcontent' => new external_value(PARAM_RAW, 'Page contents.', VALUE_OPTIONAL),
598 'contentformat' => new external_format_value('cachedcontent', VALUE_OPTIONAL),
599 'contentsize' => new external_value(PARAM_INT, 'Size of page contents in bytes (doesn\'t include'.
600 ' size of attached files).', VALUE_OPTIONAL),
604 'warnings' => new external_warnings(),
610 * Describes the parameters for get_page_contents.
612 * @return external_function_parameters
615 public static function get_page_contents_parameters() {
616 return new external_function_parameters (
618 'pageid' => new external_value(PARAM_INT, 'Page ID.')
624 * Get a page contents.
626 * @param int $pageid The page ID.
627 * @return array of warnings and page data.
630 public static function get_page_contents($pageid) {
632 $params = self::validate_parameters(self::get_page_contents_parameters(),
640 if (!$page = wiki_get_page($params['pageid'])) {
641 throw new moodle_exception('incorrectpageid', 'wiki');
644 // Get wiki instance.
645 if (!$wiki = wiki_get_wiki_from_pageid($params['pageid'])) {
646 throw new moodle_exception('incorrectwikiid', 'wiki');
649 // Permission validation.
650 $cm = get_coursemodule_from_instance('wiki', $wiki->id, $wiki->course);
651 $context = context_module::instance($cm->id);
652 self::validate_context($context);
654 // Check if user can view this wiki.
655 if (!$subwiki = wiki_get_subwiki($page->subwikiid)) {
656 throw new moodle_exception('incorrectsubwikiid', 'wiki');
658 if (!wiki_user_can_view($subwiki, $wiki)) {
659 throw new moodle_exception('cannotviewpage', 'wiki');
662 $returnedpage = array();
663 $returnedpage['id'] = $page->id;
664 $returnedpage['wikiid'] = $wiki->id;
665 $returnedpage['subwikiid'] = $page->subwikiid;
666 $returnedpage['groupid'] = $subwiki->groupid;
667 $returnedpage['userid'] = $subwiki->userid;
668 $returnedpage['title'] = $page->title;
670 // Refresh page cached content if needed.
671 if ($page->timerendered + WIKI_REFRESH_CACHE_TIME < time()) {
672 if ($content = wiki_refresh_cachedcontent($page)) {
673 $page = $content['page'];
677 list($returnedpage['cachedcontent'], $returnedpage['contentformat']) = external_format_text(
678 $page->cachedcontent, FORMAT_HTML, $context->id, 'mod_wiki', 'attachments', $subwiki->id);
679 $returnedpage['caneditpage'] = wiki_user_can_edit($subwiki);
682 $result['page'] = $returnedpage;
683 $result['warnings'] = $warnings;
688 * Describes the get_page_contents return value.
690 * @return external_single_structure
693 public static function get_page_contents_returns() {
694 return new external_single_structure(
696 'page' => new external_single_structure(
698 'id' => new external_value(PARAM_INT, 'Page ID.'),
699 'wikiid' => new external_value(PARAM_INT, 'Page\'s wiki ID.'),
700 'subwikiid' => new external_value(PARAM_INT, 'Page\'s subwiki ID.'),
701 'groupid' => new external_value(PARAM_INT, 'Page\'s group ID.'),
702 'userid' => new external_value(PARAM_INT, 'Page\'s user ID.'),
703 'title' => new external_value(PARAM_RAW, 'Page title.'),
704 'cachedcontent' => new external_value(PARAM_RAW, 'Page contents.'),
705 'contentformat' => new external_format_value('cachedcontent', VALUE_OPTIONAL),
706 'caneditpage' => new external_value(PARAM_BOOL, 'True if user can edit the page.')
709 'warnings' => new external_warnings()