From 66ef16d3647f14d86211838aff83d102f8358e55 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 20 Sep 2017 09:29:45 +0200 Subject: [PATCH] MDL-58713 filters: New WS core_filters_get_available_in_context --- filter/classes/external.php | 134 +++++++++++++++++++++++ filter/tests/external_test.php | 191 +++++++++++++++++++++++++++++++++ lib/db/services.php | 9 ++ version.php | 2 +- 4 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 filter/classes/external.php create mode 100644 filter/tests/external_test.php diff --git a/filter/classes/external.php b/filter/classes/external.php new file mode 100644 index 00000000000..ed6218f9763 --- /dev/null +++ b/filter/classes/external.php @@ -0,0 +1,134 @@ +. + +/** + * This is the external API for the filter component. + * + * @package core_filters + * @copyright 2017 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_filters; +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/externallib.php'); +require_once($CFG->libdir . '/filterlib.php'); + +use external_api; +use external_function_parameters; +use external_value; +use external_single_structure; +use external_multiple_structure; +use external_warnings; +use Exception; + +/** + * This is the external API for the filter component. + * + * @copyright 2017 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class external extends external_api { + + /** + * Returns description of get_available_in_context() parameters. + * + * @return external_function_parameters + * @since Moodle 3.4 + */ + public static function get_available_in_context_parameters() { + return new external_function_parameters ( + array( + 'contexts' => new external_multiple_structure( + new external_single_structure( + array( + 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level where the filters are: + (coursecat, course, module)'), + 'instanceid' => new external_value(PARAM_INT, 'The instance id of item associated with the context.') + ) + ), 'The list of contexts to check.' + ), + ) + ); + } + + /** + * Returns the filters available in the given contexts. + * + * @param array $contexts the list of contexts to check + * @return array with the filters information and warnings + * @since Moodle 3.4 + */ + public static function get_available_in_context($contexts) { + $params = self::validate_parameters(self::get_available_in_context_parameters(), array('contexts' => $contexts)); + $filters = $warnings = array(); + + foreach ($params['contexts'] as $contextinfo) { + try { + $context = self::get_context_from_params($contextinfo); + self::validate_context($context); + $contextinfo['contextid'] = $context->id; + } catch (Exception $e) { + $warnings[] = array( + 'item' => 'context', + 'itemid' => $context['instanceid'], + 'warningcode' => $e->getCode(), + 'message' => $e->getMessage(), + ); + continue; + } + $contextfilters = filter_get_available_in_context($context); + + foreach ($contextfilters as $filter) { + $filters[] = array_merge($contextinfo, (array) $filter); + } + } + + return array( + 'filters' => $filters, + 'warnings' => $warnings, + ); + } + + /** + * Returns description of get_available_in_context() result value. + * + * @return external_single_structure + * @since Moodle 3.4 + */ + public static function get_available_in_context_returns() { + return new external_single_structure( + array( + 'filters' => new external_multiple_structure( + new external_single_structure( + array( + 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level where the filters are: + (coursecat, course, module).'), + 'instanceid' => new external_value(PARAM_INT, 'The instance id of item associated with the context.'), + 'contextid' => new external_value(PARAM_INT, 'The context id.'), + 'filter' => new external_value(PARAM_PLUGIN, 'Filter plugin name.'), + 'localstate' => new external_value(PARAM_INT, 'Filter state: 1 for on, -1 for off, 0 if inherit.'), + 'inheritedstate' => new external_value(PARAM_INT, '1 or 0 to use when localstate is set to inherit.'), + ) + ), + 'Available filters' + ), + 'warnings' => new external_warnings(), + ) + ); + } +} diff --git a/filter/tests/external_test.php b/filter/tests/external_test.php new file mode 100644 index 00000000000..71bdef4d48f --- /dev/null +++ b/filter/tests/external_test.php @@ -0,0 +1,191 @@ +. + +/** + * External filter functions unit tests. + * + * @package core_filters + * @category external + * @copyright 2017 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 3.4 + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; + +require_once($CFG->dirroot . '/webservice/tests/helpers.php'); +use core_filters\external; + +/** + * External filter functions unit tests. + * + * @package core_filters + * @category external + * @copyright 2017 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 3.4 + */ +class core_filter_external_testcase extends externallib_advanced_testcase { + + /** + * Test get_available_in_context_system + */ + public function test_get_available_in_context_system() { + global $DB; + + $this->resetAfterTest(true); + $this->setAdminUser(); + + $this->expectException('moodle_exception'); + external::get_available_in_context(array(array('contextlevel' => 'system', 'instanceid' => 0))); + } + + /** + * Test get_available_in_context_category + */ + public function test_get_available_in_context_category() { + global $DB; + + $this->resetAfterTest(true); + $this->setAdminUser(); + + $category = self::getDataGenerator()->create_category(); + + // Get all filters and disable them all globally. + $allfilters = filter_get_all_installed(); + foreach ($allfilters as $filter => $filtername) { + filter_set_global_state($filter, TEXTFILTER_DISABLED); + } + + $result = external::get_available_in_context(array(array('contextlevel' => 'coursecat', 'instanceid' => $category->id))); + $result = external_api::clean_returnvalue(external::get_available_in_context_returns(), $result); + $this->assertEmpty($result['filters']); // No filters, all disabled. + $this->assertEmpty($result['warnings']); + + // Enable one filter at global level. + reset($allfilters); + $firstfilter = key($allfilters); + filter_set_global_state($firstfilter, TEXTFILTER_ON); + + $result = external::get_available_in_context(array(array('contextlevel' => 'coursecat', 'instanceid' => $category->id))); + $result = external_api::clean_returnvalue(external::get_available_in_context_returns(), $result); + $this->assertEmpty($result['warnings']); + $this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled. + $this->assertEquals(TEXTFILTER_INHERIT, $result['filters'][0]['localstate']); // Inherits the parent context status. + $this->assertEquals(TEXTFILTER_ON, $result['filters'][0]['inheritedstate']); // In the parent context is available. + + // Set off the same filter at local context level. + filter_set_local_state($firstfilter, context_coursecat::instance($category->id)->id, TEXTFILTER_OFF); + $result = external::get_available_in_context(array(array('contextlevel' => 'coursecat', 'instanceid' => $category->id))); + $result = external_api::clean_returnvalue(external::get_available_in_context_returns(), $result); + $this->assertEmpty($result['warnings']); + $this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled globally. + $this->assertEquals(TEXTFILTER_OFF, $result['filters'][0]['localstate']); // It is not available in this context. + $this->assertEquals(TEXTFILTER_ON, $result['filters'][0]['inheritedstate']); // In the parent context is available. + } + + /** + * Test get_available_in_context_course + */ + public function test_get_available_in_context_course() { + global $DB; + + $this->resetAfterTest(true); + $this->setAdminUser(); + + $course = self::getDataGenerator()->create_course(); + + // Get all filters and disable them all globally. + $allfilters = filter_get_all_installed(); + foreach ($allfilters as $filter => $filtername) { + filter_set_global_state($filter, TEXTFILTER_DISABLED); + } + + $result = external::get_available_in_context(array(array('contextlevel' => 'course', 'instanceid' => $course->id))); + $result = external_api::clean_returnvalue(external::get_available_in_context_returns(), $result); + $this->assertEmpty($result['filters']); // No filters, all disabled at global level. + $this->assertEmpty($result['warnings']); + + // Enable one filter at global level. + reset($allfilters); + $firstfilter = key($allfilters); + filter_set_global_state($firstfilter, TEXTFILTER_ON); + + $result = external::get_available_in_context(array(array('contextlevel' => 'course', 'instanceid' => $course->id))); + $result = external_api::clean_returnvalue(external::get_available_in_context_returns(), $result); + $this->assertEmpty($result['warnings']); + $this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled. + $this->assertEquals(TEXTFILTER_INHERIT, $result['filters'][0]['localstate']); // Inherits the parent context status. + $this->assertEquals(TEXTFILTER_ON, $result['filters'][0]['inheritedstate']); // In the parent context is available. + + // Set off the same filter at local context level. + filter_set_local_state($firstfilter, context_course::instance($course->id)->id, TEXTFILTER_OFF); + $result = external::get_available_in_context(array(array('contextlevel' => 'course', 'instanceid' => $course->id))); + $result = external_api::clean_returnvalue(external::get_available_in_context_returns(), $result); + $this->assertEmpty($result['warnings']); + $this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled globally. + $this->assertEquals(TEXTFILTER_OFF, $result['filters'][0]['localstate']); // It is not available in this context. + $this->assertEquals(TEXTFILTER_ON, $result['filters'][0]['inheritedstate']); // In the parent context is available. + } + + /** + * Test get_available_in_context_module + */ + public function test_get_available_in_context_module() { + global $DB; + + $this->resetAfterTest(true); + $this->setAdminUser(); + + // Create one activity. + $course = self::getDataGenerator()->create_course(); + $forum = self::getDataGenerator()->create_module('forum', (object) array('course' => $course->id)); + + // Get all filters and disable them all globally. + $allfilters = filter_get_all_installed(); + foreach ($allfilters as $filter => $filtername) { + filter_set_global_state($filter, TEXTFILTER_DISABLED); + } + + $result = external::get_available_in_context(array(array('contextlevel' => 'module', 'instanceid' => $forum->cmid))); + $result = external_api::clean_returnvalue(external::get_available_in_context_returns(), $result); + $this->assertEmpty($result['filters']); // No filters, all disabled at global level. + $this->assertEmpty($result['warnings']); + + // Enable one filter at global level. + reset($allfilters); + $firstfilter = key($allfilters); + filter_set_global_state($firstfilter, TEXTFILTER_ON); + + $result = external::get_available_in_context(array(array('contextlevel' => 'module', 'instanceid' => $forum->cmid))); + $result = external_api::clean_returnvalue(external::get_available_in_context_returns(), $result); + $this->assertEmpty($result['warnings']); + $this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled. + $this->assertEquals(TEXTFILTER_INHERIT, $result['filters'][0]['localstate']); // Inherits the parent context status. + $this->assertEquals(TEXTFILTER_ON, $result['filters'][0]['inheritedstate']); // In the parent context is available. + + // Set off the same filter at local context level. + filter_set_local_state($firstfilter, context_module::instance($forum->cmid)->id, TEXTFILTER_OFF); + $result = external::get_available_in_context(array(array('contextlevel' => 'module', 'instanceid' => $forum->cmid))); + $result = external_api::clean_returnvalue(external::get_available_in_context_returns(), $result); + $this->assertEmpty($result['warnings']); + $this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled globally. + $this->assertEquals(TEXTFILTER_OFF, $result['filters'][0]['localstate']); // It is not available in this context. + $this->assertEquals(TEXTFILTER_ON, $result['filters'][0]['inheritedstate']); // In the parent context is available. + } +} diff --git a/lib/db/services.php b/lib/db/services.php index 2f2814e8c16..34425f6262a 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -2075,6 +2075,15 @@ $functions = array( 'capabilities' => '', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), + + // Filters functions. + 'core_filters_get_available_in_context' => array( + 'classname' => 'core_filters\external', + 'methodname' => 'get_available_in_context', + 'description' => 'Returns the filters available in the given contexts.', + 'type' => 'read', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + ) ); $services = array( diff --git a/version.php b/version.php index f2d9fcc0091..5cf6c98e6ac 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2017101300.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2017101300.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes.