diff --git a/admin/tool/dataprivacy/classes/external.php b/admin/tool/dataprivacy/classes/external.php index 4c4e71944ab..7306eb20d9d 100644 --- a/admin/tool/dataprivacy/classes/external.php +++ b/admin/tool/dataprivacy/classes/external.php @@ -1613,7 +1613,7 @@ class external extends external_api { */ private static function get_tree_node_structure($allowchildbranches = true) { $fields = [ - 'text' => new external_value(PARAM_TEXT, 'The node text', VALUE_REQUIRED), + 'text' => new external_value(PARAM_RAW, 'The node text', VALUE_REQUIRED), 'expandcontextid' => new external_value(PARAM_INT, 'The contextid this node expands', VALUE_REQUIRED), 'expandelement' => new external_value(PARAM_ALPHA, 'What element is this node expanded to', VALUE_REQUIRED), 'contextid' => new external_value(PARAM_INT, 'The node contextid', VALUE_REQUIRED), diff --git a/admin/tool/mobile/classes/external.php b/admin/tool/mobile/classes/external.php index b3c56bb3e01..8b9057c0f04 100644 --- a/admin/tool/mobile/classes/external.php +++ b/admin/tool/mobile/classes/external.php @@ -139,7 +139,7 @@ class external extends external_api { array( 'wwwroot' => new external_value(PARAM_RAW, 'Site URL.'), 'httpswwwroot' => new external_value(PARAM_RAW, 'Site https URL (if httpslogin is enabled).'), - 'sitename' => new external_value(PARAM_TEXT, 'Site name.'), + 'sitename' => new external_value(PARAM_RAW, 'Site name.'), 'guestlogin' => new external_value(PARAM_INT, 'Whether guest login is enabled.'), 'rememberusername' => new external_value(PARAM_INT, 'Values: 0 for No, 1 for Yes, 2 for optional.'), 'authloginviaemail' => new external_value(PARAM_INT, 'Whether log in via email is enabled.'), diff --git a/auth/email/classes/external.php b/auth/email/classes/external.php index 782f0336c4e..52e7227bae7 100644 --- a/auth/email/classes/external.php +++ b/auth/email/classes/external.php @@ -147,12 +147,12 @@ class auth_email_external extends external_api { array( 'id' => new external_value(PARAM_INT, 'Profile field id', VALUE_OPTIONAL), 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'Profile field shortname', VALUE_OPTIONAL), - 'name' => new external_value(PARAM_TEXT, 'Profield field name', VALUE_OPTIONAL), + 'name' => new external_value(PARAM_RAW, 'Profield field name', VALUE_OPTIONAL), 'datatype' => new external_value(PARAM_ALPHANUMEXT, 'Profield field datatype', VALUE_OPTIONAL), 'description' => new external_value(PARAM_RAW, 'Profield field description', VALUE_OPTIONAL), 'descriptionformat' => new external_format_value('description'), 'categoryid' => new external_value(PARAM_INT, 'Profield field category id', VALUE_OPTIONAL), - 'categoryname' => new external_value(PARAM_TEXT, 'Profield field category name', VALUE_OPTIONAL), + 'categoryname' => new external_value(PARAM_RAW, 'Profield field category name', VALUE_OPTIONAL), 'sortorder' => new external_value(PARAM_INT, 'Profield field sort order', VALUE_OPTIONAL), 'required' => new external_value(PARAM_INT, 'Profield field required', VALUE_OPTIONAL), 'locked' => new external_value(PARAM_INT, 'Profield field locked', VALUE_OPTIONAL), diff --git a/auth/email/tests/external_test.php b/auth/email/tests/external_test.php index 0220640e9da..f62f95c1102 100644 --- a/auth/email/tests/external_test.php +++ b/auth/email/tests/external_test.php @@ -93,6 +93,51 @@ class auth_email_external_testcase extends externallib_advanced_testcase { $this->assertEquals('textarea', $namedarray['sometext']['datatype']); } + /** + * Test get_signup_settings with mathjax in a profile field. + */ + public function test_get_signup_settings_with_mathjax_in_profile_fields() { + global $CFG, $DB; + + require_once($CFG->dirroot . '/lib/externallib.php'); + + // Enable MathJax filter in content and headings. + $this->configure_filters([ + ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true], + ]); + + // Create category with MathJax and a new field with MathJax. + $categoryname = 'Cat $$(a+b)=2$$'; + $fieldname = 'Some text $$(a+b)=2$$'; + $categoryid = $DB->insert_record('user_info_category', array('name' => $categoryname, 'sortorder' => 1)); + $field3 = $DB->insert_record('user_info_field', array( + 'shortname' => 'mathjaxname', 'name' => $fieldname, 'categoryid' => $categoryid, + 'datatype' => 'textarea', 'signup' => 1, 'visible' => 1, 'required' => 1, 'sortorder' => 2)); + + $result = auth_email_external::get_signup_settings(); + $result = external_api::clean_returnvalue(auth_email_external::get_signup_settings_returns(), $result); + + // Format the original data. + $sitecontext = context_system::instance(); + $categoryname = external_format_string($categoryname, $sitecontext->id); + $fieldname = external_format_string($fieldname, $sitecontext->id); + + // Whip up a array with named entries to easily check against. + $namedarray = array(); + foreach ($result['profilefields'] as $key => $value) { + $namedarray[$value['shortname']] = $value; + } + + // Check the new profile field. + $this->assertArrayHasKey('mathjaxname', $namedarray); + $this->assertStringContainsString('', + $namedarray['mathjaxname']['categoryname']); + $this->assertStringContainsString('', + $namedarray['mathjaxname']['name']); + $this->assertEquals($categoryname, $namedarray['mathjaxname']['categoryname']); + $this->assertEquals($fieldname, $namedarray['mathjaxname']['name']); + } + public function test_signup_user() { global $DB; diff --git a/blocks/classes/external.php b/blocks/classes/external.php index e9f09ac3837..cc0818097b6 100644 --- a/blocks/classes/external.php +++ b/blocks/classes/external.php @@ -59,7 +59,7 @@ class core_block_external extends external_api { 'visible' => new external_value(PARAM_BOOL, 'Whether the block is visible.', VALUE_OPTIONAL), 'contents' => new external_single_structure( array( - 'title' => new external_value(PARAM_TEXT, 'Block title.'), + 'title' => new external_value(PARAM_RAW, 'Block title.'), 'content' => new external_value(PARAM_RAW, 'Block contents.'), 'contentformat' => new external_format_value('content'), 'footer' => new external_value(PARAM_RAW, 'Block footer.'), diff --git a/blocks/tests/externallib_test.php b/blocks/tests/externallib_test.php index ee1b61b9db9..f5bb942308d 100644 --- a/blocks/tests/externallib_test.php +++ b/blocks/tests/externallib_test.php @@ -235,6 +235,85 @@ class core_block_externallib_testcase extends externallib_advanced_testcase { $this->assertEquals(5, $configcounts); } + /** + * Test get_course_blocks contents with mathjax. + */ + public function test_get_course_blocks_contents_with_mathjax() { + global $DB, $CFG; + + require_once($CFG->dirroot . '/lib/externallib.php'); + + $this->resetAfterTest(true); + + // Enable MathJax filter in content and headings. + $this->configure_filters([ + ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true], + ]); + + // Create a few stuff to test with. + $user = $this->getDataGenerator()->create_user(); + $course = $this->getDataGenerator()->create_course(); + $studentrole = $DB->get_record('role', array('shortname' => 'student')); + $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id); + $coursecontext = context_course::instance($course->id); + + // Create a HTML block. + $title = 'My block $$(a+b)=2$$'; + $body = 'My block contents $$(a+b)=2$$'; + $bodyformat = FORMAT_MOODLE; + $page = new moodle_page(); + $page->set_context($coursecontext); + $page->set_pagelayout('course'); + $course->format = course_get_format($course)->get_format(); + $page->set_pagetype('course-view-' . $course->format); + $page->blocks->load_blocks(); + $newblock = 'html'; + $page->blocks->add_block_at_end_of_default_region($newblock); + + $this->setUser($user); + // Re-create the page. + $page = new moodle_page(); + $page->set_context($coursecontext); + $page->set_pagelayout('course'); + $course->format = course_get_format($course)->get_format(); + $page->set_pagetype('course-view-' . $course->format); + $page->blocks->load_blocks(); + $blocks = $page->blocks->get_blocks_for_region($page->blocks->get_default_region()); + $block = end($blocks); + $block = block_instance('html', $block->instance); + $nonscalar = [ + 'something' => true, + ]; + $configdata = (object) [ + 'title' => $title, + 'text' => [ + 'itemid' => 0, + 'text' => $body, + 'format' => $bodyformat, + ], + 'nonscalar' => $nonscalar + ]; + $block->instance_config_save((object) $configdata); + + // Check for the new block. + $result = core_block_external::get_course_blocks($course->id, true); + $result = external_api::clean_returnvalue(core_block_external::get_course_blocks_returns(), $result); + + // Format the original data. + $sitecontext = context_system::instance(); + $title = external_format_string($title, $coursecontext->id); + list($body, $bodyformat) = external_format_text($body, $bodyformat, $coursecontext->id, 'block_html', 'content'); + + // Check that the block data is formatted. + $this->assertCount(1, $result['blocks']); + $this->assertStringContainsString('', + $result['blocks'][0]['contents']['title']); + $this->assertStringContainsString('', + $result['blocks'][0]['contents']['content']); + $this->assertEquals($title, $result['blocks'][0]['contents']['title']); + $this->assertEquals($body, $result['blocks'][0]['contents']['content']); + } + /** * Test user get default dashboard blocks. */ diff --git a/calendar/externallib.php b/calendar/externallib.php index 3f186f1ee86..b2c94b546b0 100644 --- a/calendar/externallib.php +++ b/calendar/externallib.php @@ -319,6 +319,7 @@ class core_calendar_external extends external_api { $event = (array) $eventobj; // Description formatting. $calendareventobj = new calendar_event($event); + $event['name'] = $calendareventobj->format_external_name(); list($event['description'], $event['format']) = $calendareventobj->format_external_text(); if ($hassystemcap) { @@ -365,7 +366,7 @@ class core_calendar_external extends external_api { 'events' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'event id'), - 'name' => new external_value(PARAM_TEXT, 'event name'), + 'name' => new external_value(PARAM_RAW, 'event name'), 'description' => new external_value(PARAM_RAW, 'Description', VALUE_OPTIONAL, null, NULL_ALLOWED), 'format' => new external_format_value('description'), 'courseid' => new external_value(PARAM_INT, 'course id'), @@ -745,7 +746,7 @@ class core_calendar_external extends external_api { 'events' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'event id'), - 'name' => new external_value(PARAM_TEXT, 'event name'), + 'name' => new external_value(PARAM_RAW, 'event name'), 'description' => new external_value(PARAM_RAW, 'Description', VALUE_OPTIONAL), 'format' => new external_format_value('description'), 'courseid' => new external_value(PARAM_INT, 'course id'), diff --git a/calendar/lib.php b/calendar/lib.php index aba329f6db8..1fceea977d4 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -967,6 +967,22 @@ class calendar_event { } } + /** + * Format the event name using the external API. + * + * This function should we used when text formatting is required in external functions. + * + * @return string Formatted name. + */ + public function format_external_name() { + if ($this->editorcontext === null) { + // Switch on the event type to decide upon the appropriate context to use for this event. + $this->editorcontext = $this->get_context(); + } + + return external_format_string($this->properties->name, $this->editorcontext->id); + } + /** * Format the text using the external API. * diff --git a/calendar/tests/externallib_test.php b/calendar/tests/externallib_test.php index fc5ea7ec35c..1d41b310bcb 100644 --- a/calendar/tests/externallib_test.php +++ b/calendar/tests/externallib_test.php @@ -542,6 +542,42 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase { $this->assertEquals($category2->id, $events['events'][1]['categoryid']); } + /** + * Test get_calendar_events with mathjax in the name. + */ + public function test_get_calendar_events_with_mathjax() { + global $USER; + + $this->resetAfterTest(true); + set_config('calendar_adminseesall', 1); + $this->setAdminUser(); + + // Enable MathJax filter in content and headings. + $this->configure_filters([ + ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true], + ]); + + // Create a site event with mathjax in the name and description. + $siteevent = $this->create_calendar_event('Site Event $$(a+b)=2$$', $USER->id, 'site', 0, time(), + ['description' => 'Site Event Description $$(a+b)=2$$']); + + // Now call the WebService. + $events = core_calendar_external::get_calendar_events(); + $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); + + // Format the original data. + $sitecontext = context_system::instance(); + $siteevent->name = $siteevent->format_external_name(); + list($siteevent->description, $siteevent->descriptionformat) = $siteevent->format_external_text(); + + // Check that the event data is formatted. + $this->assertCount(1, $events['events']); + $this->assertStringContainsString('', $events['events'][0]['name']); + $this->assertStringContainsString('', $events['events'][0]['description']); + $this->assertEquals($siteevent->name, $events['events'][0]['name']); + $this->assertEquals($siteevent->description, $events['events'][0]['description']); + } + /** * Test core_calendar_external::create_calendar_events */ diff --git a/course/externallib.php b/course/externallib.php index 74fa9871cd6..a4140fb79c3 100644 --- a/course/externallib.php +++ b/course/externallib.php @@ -426,7 +426,7 @@ class core_course_external extends external_api { new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'Section ID'), - 'name' => new external_value(PARAM_TEXT, 'Section name'), + 'name' => new external_value(PARAM_RAW, 'Section name'), 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL), 'summary' => new external_value(PARAM_RAW, 'Section description'), 'summaryformat' => new external_format_value('summary'), @@ -669,12 +669,12 @@ class core_course_external extends external_api { new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'course id'), - 'shortname' => new external_value(PARAM_TEXT, 'course short name'), + 'shortname' => new external_value(PARAM_RAW, 'course short name'), 'categoryid' => new external_value(PARAM_INT, 'category id'), 'categorysortorder' => new external_value(PARAM_INT, 'sort order into the category', VALUE_OPTIONAL), - 'fullname' => new external_value(PARAM_TEXT, 'full name'), - 'displayname' => new external_value(PARAM_TEXT, 'course display name'), + 'fullname' => new external_value(PARAM_RAW, 'full name'), + 'displayname' => new external_value(PARAM_RAW, 'course display name'), 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), 'summary' => new external_value(PARAM_RAW, 'summary'), 'summaryformat' => new external_format_value('summary'), @@ -729,7 +729,7 @@ class core_course_external extends external_api { ), 'customfields' => new external_multiple_structure( new external_single_structure( - ['name' => new external_value(PARAM_TEXT, 'The name of the custom field'), + ['name' => new external_value(PARAM_RAW, 'The name of the custom field'), 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'), 'type' => new external_value(PARAM_COMPONENT, 'The type of the custom field - text, checkbox...'), @@ -939,7 +939,7 @@ class core_course_external extends external_api { new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'course id'), - 'shortname' => new external_value(PARAM_TEXT, 'short name'), + 'shortname' => new external_value(PARAM_RAW, 'short name'), ) ) ); @@ -1485,7 +1485,7 @@ class core_course_external extends external_api { return new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'course id'), - 'shortname' => new external_value(PARAM_TEXT, 'short name'), + 'shortname' => new external_value(PARAM_RAW, 'short name'), ) ); } @@ -1963,7 +1963,7 @@ class core_course_external extends external_api { new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'category id'), - 'name' => new external_value(PARAM_TEXT, 'category name'), + 'name' => new external_value(PARAM_RAW, 'category name'), 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL), 'description' => new external_value(PARAM_RAW, 'category description'), 'descriptionformat' => new external_format_value('description'), @@ -2069,7 +2069,7 @@ class core_course_external extends external_api { new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'new category id'), - 'name' => new external_value(PARAM_TEXT, 'new category name'), + 'name' => new external_value(PARAM_RAW, 'new category name'), ) ) ); @@ -2607,11 +2607,11 @@ class core_course_external extends external_api { protected static function get_course_structure($onlypublicdata = true) { $coursestructure = array( 'id' => new external_value(PARAM_INT, 'course id'), - 'fullname' => new external_value(PARAM_TEXT, 'course full name'), - 'displayname' => new external_value(PARAM_TEXT, 'course display name'), - 'shortname' => new external_value(PARAM_TEXT, 'course short name'), + 'fullname' => new external_value(PARAM_RAW, 'course full name'), + 'displayname' => new external_value(PARAM_RAW, 'course display name'), + 'shortname' => new external_value(PARAM_RAW, 'course short name'), 'categoryid' => new external_value(PARAM_INT, 'category id'), - 'categoryname' => new external_value(PARAM_TEXT, 'category name'), + 'categoryname' => new external_value(PARAM_RAW, 'category name'), 'sortorder' => new external_value(PARAM_INT, 'Sort order in the category', VALUE_OPTIONAL), 'summary' => new external_value(PARAM_RAW, 'summary'), 'summaryformat' => new external_format_value('summary'), @@ -2861,7 +2861,7 @@ class core_course_external extends external_api { new external_single_structure( array( 'id' => new external_value(PARAM_ALPHANUMEXT, 'Outcome id'), - 'name' => new external_value(PARAM_TEXT, 'Outcome full name'), + 'name' => new external_value(PARAM_RAW, 'Outcome full name'), 'scale' => new external_value(PARAM_TEXT, 'Scale items') ) ), diff --git a/enrol/externallib.php b/enrol/externallib.php index 3d1f03f7450..d0fbaa9453a 100644 --- a/enrol/externallib.php +++ b/enrol/externallib.php @@ -460,7 +460,7 @@ class core_enrol_external extends external_api { 'id' => new external_value(PARAM_INT, 'id of course'), 'shortname' => new external_value(PARAM_RAW, 'short name of course'), 'fullname' => new external_value(PARAM_RAW, 'long name of course'), - 'displayname' => new external_value(PARAM_TEXT, 'course display name for lists.', VALUE_OPTIONAL), + 'displayname' => new external_value(PARAM_RAW, 'course display name for lists.', VALUE_OPTIONAL), 'enrolledusercount' => new external_value(PARAM_INT, 'Number of enrolled users in this course', VALUE_OPTIONAL), 'idnumber' => new external_value(PARAM_RAW, 'id number of course'), diff --git a/enrol/tests/externallib_test.php b/enrol/tests/externallib_test.php index baf1d70a7f9..3a1b1de3b85 100644 --- a/enrol/tests/externallib_test.php +++ b/enrol/tests/externallib_test.php @@ -521,6 +521,61 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase { $this->assertEquals(0, $enrolledincourses[0]['lastaccess']); // I can't see this, hidden by global setting. } + /** + * Test get_users_courses with mathjax in the name. + */ + public function test_get_users_courses_with_mathjax() { + global $DB; + + $this->resetAfterTest(true); + + // Enable MathJax filter in content and headings. + $this->configure_filters([ + ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true], + ]); + + // Create a course with MathJax in the name and summary. + $coursedata = [ + 'fullname' => 'Course 1 $$(a+b)=2$$', + 'shortname' => 'Course 1 $$(a+b)=2$$', + 'summary' => 'Lightwork Course 1 description $$(a+b)=2$$', + 'summaryformat' => FORMAT_HTML, + ]; + + $course = self::getDataGenerator()->create_course($coursedata); + $context = context_course::instance($course->id); + + // Enrol a student in the course. + $student = $this->getDataGenerator()->create_user(); + $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student']); + $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentroleid); + + $this->setUser($student); + + // Call the external function. + $enrolledincourses = core_enrol_external::get_users_courses($student->id, true); + + // We need to execute the return values cleaning process to simulate the web service server. + $enrolledincourses = external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); + + // Check that the amount of courses is the right one. + $this->assertCount(1, $enrolledincourses); + + // Filter the values to compare them with the returned ones. + $course->fullname = external_format_string($course->fullname, $context->id); + $course->shortname = external_format_string($course->shortname, $context->id); + list($course->summary, $course->summaryformat) = + external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0); + + // Compare the values. + $this->assertStringContainsString('', $enrolledincourses[0]['fullname']); + $this->assertStringContainsString('', $enrolledincourses[0]['shortname']); + $this->assertStringContainsString('', $enrolledincourses[0]['summary']); + $this->assertEquals($course->fullname, $enrolledincourses[0]['fullname']); + $this->assertEquals($course->shortname, $enrolledincourses[0]['shortname']); + $this->assertEquals($course->summary, $enrolledincourses[0]['summary']); + } + /** * Test get_course_enrolment_methods */ diff --git a/lib/external/externallib.php b/lib/external/externallib.php index c8e08abbb20..7bc8ab6b56a 100644 --- a/lib/external/externallib.php +++ b/lib/external/externallib.php @@ -497,7 +497,7 @@ class core_external extends external_api { 'value' => new external_value(PARAM_RAW, 'value of the item as it is stored', VALUE_OPTIONAL), 'itemid' => new external_value(PARAM_RAW, 'identifier of the updated item', VALUE_OPTIONAL), 'edithint' => new external_value(PARAM_NOTAGS, 'hint for editing element', VALUE_OPTIONAL), - 'editlabel' => new external_value(PARAM_NOTAGS, 'label for editing element', VALUE_OPTIONAL), + 'editlabel' => new external_value(PARAM_RAW, 'label for editing element', VALUE_OPTIONAL), 'type' => new external_value(PARAM_ALPHA, 'type of the element (text, toggle, select)', VALUE_OPTIONAL), 'options' => new external_value(PARAM_RAW, 'options of the element, format depends on type', VALUE_OPTIONAL), 'linkeverything' => new external_value(PARAM_INT, 'Should everything be wrapped in the edit link or link displayed separately', VALUE_OPTIONAL), diff --git a/lib/external/tests/external_test.php b/lib/external/tests/external_test.php index bf43f49b87f..788371afdab 100644 --- a/lib/external/tests/external_test.php +++ b/lib/external/tests/external_test.php @@ -199,9 +199,41 @@ class core_external_testcase extends externallib_advanced_testcase { $tag = $this->getDataGenerator()->create_tag(); $res = core_external::update_inplace_editable('core_tag', 'tagname', $tag->id, 'new tag name'); $res = external_api::clean_returnvalue(core_external::update_inplace_editable_returns(), $res); + $this->assertEquals('new tag name', $res['value']); } + /** + * Test update_inplace_editable with mathjax. + */ + public function test_update_inplace_editable_with_mathjax() { + $this->resetAfterTest(true); + $this->setAdminUser(); + + // Enable MathJax filter in content and headings. + $this->configure_filters([ + ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true], + ]); + + // Create a forum. + $course = $this->getDataGenerator()->create_course(); + $forum = self::getDataGenerator()->create_module('forum', array('course' => $course->id, 'name' => 'forum name')); + + // Change the forum name. + $newname = 'New forum name $$(a+b)=2$$'; + $res = core_external::update_inplace_editable('core_course', 'activityname', $forum->cmid, $newname); + $res = external_api::clean_returnvalue(core_external::update_inplace_editable_returns(), $res); + + // Format original data. + $context = context_module::instance($forum->cmid); + $newname = external_format_string($newname, $context->id); + $editlabel = get_string('newactivityname', '', $newname); + + // Check editlabel is the same and has mathjax. + $this->assertStringContainsString('', $res['editlabel']); + $this->assertEquals($editlabel, $res['editlabel']); + } + public function test_get_user_dates() { $this->resetAfterTest(); diff --git a/lib/phpunit/classes/util.php b/lib/phpunit/classes/util.php index 844cc74ba94..94ec4414dbe 100644 --- a/lib/phpunit/classes/util.php +++ b/lib/phpunit/classes/util.php @@ -102,7 +102,7 @@ class phpunit_util extends testing_util { * @return void */ public static function reset_all_data($detectchanges = false) { - global $DB, $CFG, $USER, $SITE, $COURSE, $PAGE, $OUTPUT, $SESSION, $FULLME; + global $DB, $CFG, $USER, $SITE, $COURSE, $PAGE, $OUTPUT, $SESSION, $FULLME, $FILTERLIB_PRIVATE; // Stop any message redirection. self::stop_message_redirection(); @@ -202,6 +202,7 @@ class phpunit_util extends testing_util { $FULLME = null; $ME = null; $SCRIPT = null; + $FILTERLIB_PRIVATE = null; // Empty sessison and set fresh new not-logged-in user. \core\session\manager::init_empty_session(); diff --git a/message/externallib.php b/message/externallib.php index 3e101d59c2f..5512c51e49d 100644 --- a/message/externallib.php +++ b/message/externallib.php @@ -1255,8 +1255,8 @@ class core_message_external extends external_api { return new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'The conversation id'), - 'name' => new external_value(PARAM_TEXT, 'The conversation name, if set', VALUE_DEFAULT, null), - 'subname' => new external_value(PARAM_TEXT, 'A subtitle for the conversation name, if set', VALUE_DEFAULT, null), + 'name' => new external_value(PARAM_RAW, 'The conversation name, if set', VALUE_DEFAULT, null), + 'subname' => new external_value(PARAM_RAW, 'A subtitle for the conversation name, if set', VALUE_DEFAULT, null), 'imageurl' => new external_value(PARAM_URL, 'A link to the conversation picture, if set', VALUE_DEFAULT, null), 'type' => new external_value(PARAM_INT, 'The type of the conversation (1=individual,2=group,3=self)'), 'membercount' => new external_value(PARAM_INT, 'Total number of conversation members'), @@ -1316,7 +1316,7 @@ class core_message_external extends external_api { array( 'id' => new external_value(PARAM_INT, 'Conversations id'), 'type' => new external_value(PARAM_INT, 'Conversation type: private or public'), - 'name' => new external_value(PARAM_TEXT, 'Multilang compatible conversation name'. VALUE_OPTIONAL), + 'name' => new external_value(PARAM_RAW, 'Multilang compatible conversation name'. VALUE_OPTIONAL), 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the conversation'), ), 'information about conversation', VALUE_OPTIONAL), 'Conversations between users', VALUE_OPTIONAL diff --git a/message/tests/externallib_test.php b/message/tests/externallib_test.php index 1f143991219..eb380251feb 100644 --- a/message/tests/externallib_test.php +++ b/message/tests/externallib_test.php @@ -5912,6 +5912,54 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { $this->assertCount(0, $conversations); } + /** + * Test that group conversations containing MathJax don't break the WebService. + */ + public function test_get_conversations_group_with_mathjax() { + $this->resetAfterTest(true); + $this->setAdminUser(); + + // Enable MathJax filter in content and headings. + $this->configure_filters([ + ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true], + ]); + + // Create some users, a course and a group with a linked conversation. + $user1 = self::getDataGenerator()->create_user(); + $user2 = self::getDataGenerator()->create_user(); + + $coursename = 'Course $$(a+b)=2$$'; + $groupname = 'Group $$(a+b)=2$$'; + $course1 = $this->getDataGenerator()->create_course(['shortname' => $coursename]); + + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + $group1 = $this->getDataGenerator()->create_group([ + 'name' => $groupname, + 'courseid' => $course1->id, + 'enablemessaging' => 1, + ]); + + // Add users to group1. + $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id)); + + // Call the WebService. + $result = core_message_external::get_conversations($user1->id, 0, 20, null, false); + $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result); + $conversations = $result['conversations']; + + // Format original data. + $coursecontext = \context_course::instance($course1->id); + $coursename = external_format_string($coursename, $coursecontext->id); + $groupname = external_format_string($groupname, $coursecontext->id); + + $this->assertStringContainsString('', $conversations[0]['name']); + $this->assertStringContainsString('', $conversations[0]['subname']); + $this->assertEquals($groupname, $conversations[0]['name']); + $this->assertEquals($coursename, $conversations[0]['subname']); + } + /** * Test verifying get_conversations when there are users in a group and/or individual conversation. The reason this * test is performed is because we do not need as much data for group conversations (saving DB calls), so we want diff --git a/mod/assign/externallib.php b/mod/assign/externallib.php index 907f7fa1552..49320c76680 100644 --- a/mod/assign/externallib.php +++ b/mod/assign/externallib.php @@ -593,8 +593,8 @@ class mod_assign_external extends external_api { return new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'course id'), - 'fullname' => new external_value(PARAM_TEXT, 'course full name'), - 'shortname' => new external_value(PARAM_TEXT, 'course short name'), + 'fullname' => new external_value(PARAM_RAW, 'course full name'), + 'shortname' => new external_value(PARAM_RAW, 'course short name'), 'timemodified' => new external_value(PARAM_INT, 'last time modified'), 'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info') ), 'course information object' diff --git a/mod/forum/externallib.php b/mod/forum/externallib.php index 23788fd3772..5151d239ef5 100644 --- a/mod/forum/externallib.php +++ b/mod/forum/externallib.php @@ -463,7 +463,7 @@ class mod_forum_external extends external_api { 'created' => new external_value(PARAM_INT, 'Creation time'), 'modified' => new external_value(PARAM_INT, 'Time modified'), 'mailed' => new external_value(PARAM_INT, 'Mailed?'), - 'subject' => new external_value(PARAM_TEXT, 'The post subject'), + 'subject' => new external_value(PARAM_RAW, 'The post subject'), 'message' => new external_value(PARAM_RAW, 'The post message'), 'messageformat' => new external_format_value('message'), 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'), @@ -712,7 +712,7 @@ class mod_forum_external extends external_api { new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'Post id'), - 'name' => new external_value(PARAM_TEXT, 'Discussion name'), + 'name' => new external_value(PARAM_RAW, 'Discussion name'), 'groupid' => new external_value(PARAM_INT, 'Group id'), 'timemodified' => new external_value(PARAM_INT, 'Time modified'), 'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'), @@ -724,7 +724,7 @@ class mod_forum_external extends external_api { 'created' => new external_value(PARAM_INT, 'Creation time'), 'modified' => new external_value(PARAM_INT, 'Time modified'), 'mailed' => new external_value(PARAM_INT, 'Mailed?'), - 'subject' => new external_value(PARAM_TEXT, 'The post subject'), + 'subject' => new external_value(PARAM_RAW, 'The post subject'), 'message' => new external_value(PARAM_RAW, 'The post message'), 'messageformat' => new external_format_value('message'), 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'), @@ -998,7 +998,7 @@ class mod_forum_external extends external_api { new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'Post id'), - 'name' => new external_value(PARAM_TEXT, 'Discussion name'), + 'name' => new external_value(PARAM_RAW, 'Discussion name'), 'groupid' => new external_value(PARAM_INT, 'Group id'), 'timemodified' => new external_value(PARAM_INT, 'Time modified'), 'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'), @@ -1010,7 +1010,7 @@ class mod_forum_external extends external_api { 'created' => new external_value(PARAM_INT, 'Creation time'), 'modified' => new external_value(PARAM_INT, 'Time modified'), 'mailed' => new external_value(PARAM_INT, 'Mailed?'), - 'subject' => new external_value(PARAM_TEXT, 'The post subject'), + 'subject' => new external_value(PARAM_RAW, 'The post subject'), 'message' => new external_value(PARAM_RAW, 'The post message'), 'messageformat' => new external_format_value('message'), 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'), diff --git a/webservice/tests/helpers.php b/webservice/tests/helpers.php index 4d0287111e1..e30a955ac49 100644 --- a/webservice/tests/helpers.php +++ b/webservice/tests/helpers.php @@ -64,6 +64,43 @@ abstract class externallib_advanced_testcase extends advanced_testcase { return $roleid; } + /** + * Configure some filters for external tests. + * + * @param array $filters Filters to enable. Each filter should contain: + * - name: name of the filter. + * - state: the state of the filter. + * - move: -1 means up, 0 means the same, 1 means down. + * - applytostrings: true to apply the filter to content and headings, false for just content. + */ + public static function configure_filters($filters) { + global $CFG; + + $filterstrings = false; + + // Enable the filters. + foreach ($filters as $filter) { + $filter = (array) $filter; + filter_set_global_state($filter['name'], $filter['state'], $filter['move']); + filter_set_applies_to_strings($filter['name'], $filter['applytostrings']); + + $filterstrings = $filterstrings || $filter['applytostrings']; + } + + // Set WS filtering. + $wssettings = external_settings::get_instance(); + $wssettings->set_filter(true); + + // Reset filter caches. + $filtermanager = filter_manager::instance(); + $filtermanager->reset_caches(); + + if ($filterstrings) { + // Don't strip tags in strings. + $CFG->formatstringstriptags = false; + } + } + /** * Unassign a capability to $USER. * diff --git a/webservice/upgrade.txt b/webservice/upgrade.txt index 6f92dd39f76..b92bbd402de 100644 --- a/webservice/upgrade.txt +++ b/webservice/upgrade.txt @@ -3,6 +3,10 @@ information provided here is intended especially for developers. This information is intended for authors of webservices, not people writing webservice clients. +=== 3.9.3 === + +* The class externallib_advanced_testcase, used in unit tests, has a new function called "configure_filters" to easily configure filters for external functions testing. + === 3.8 === * Ajax calls can now specify a cache key. This allows for better caching capabilities on servers. If a cache key