From b9e6db9ddcd65c695d94e6df0dcbf467b49f676d Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Tue, 3 Apr 2018 11:04:39 +0100 Subject: [PATCH] MDL-61449 tool_mobile: New Web Service tool_mobile_get_content --- admin/tool/mobile/classes/external.php | 128 ++++++++++++++++++ admin/tool/mobile/db/services.php | 9 +- admin/tool/mobile/tests/externallib_test.php | 50 +++++++ .../mobile/tests/fixtures/output/mobile.php | 60 ++++++++ admin/tool/mobile/version.php | 2 +- 5 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 admin/tool/mobile/tests/fixtures/output/mobile.php diff --git a/admin/tool/mobile/classes/external.php b/admin/tool/mobile/classes/external.php index 398a1a76c86a2..6a4626477f545 100644 --- a/admin/tool/mobile/classes/external.php +++ b/admin/tool/mobile/classes/external.php @@ -28,6 +28,7 @@ require_once("$CFG->libdir/externallib.php"); use external_api; +use external_files; use external_function_parameters; use external_value; use external_single_structure; @@ -37,6 +38,7 @@ use moodle_exception; use moodle_url; use core_text; +use coding_exception; /** * This is the external API for this tool. @@ -332,4 +334,130 @@ public static function get_autologin_key_returns() { ) ); } + + /** + * Returns description of get_content() parameters + * + * @return external_function_parameters + * @since Moodle 3.5 + */ + public static function get_content_parameters() { + return new external_function_parameters( + array( + 'component' => new external_value(PARAM_COMPONENT, 'Component where the class is e.g. mod_assign.'), + 'method' => new external_value(PARAM_ALPHANUMEXT, 'Method to execute in class \$component\output\mobile.'), + 'args' => new external_multiple_structure( + new external_single_structure( + array( + 'name' => new external_value(PARAM_ALPHANUMEXT, 'Param name.'), + 'value' => new external_value(PARAM_RAW, 'Param value.') + ) + ), 'Args for the method are optional.', VALUE_OPTIONAL + ) + ) + ); + } + + /** + * Returns a piece of content to be displayed in the Mobile app, it usually returns a template, javascript and + * other structured data that will be used to render a view in the Mobile app.. + * + * Callbacks (placed in \$component\output\mobile) that are called by this web service are responsible for doing the + * appropriate security checks to access the information to be returned. + * + * @param string $component fame of the component. + * @param string $method function method name in class \$component\output\mobile. + * @param array $args optional arguments for the method. + * @return array HTML, JavaScript and other required data and information to create a view in the app. + * @since Moodle 3.5 + * @throws coding_exception + */ + public static function get_content($component, $method, $args = array()) { + global $OUTPUT, $PAGE, $USER; + + $params = self::validate_parameters(self::get_content_parameters(), + array( + 'component' => $component, + 'method' => $method, + 'args' => $args + ) + ); + + // Reformat arguments into something less unwieldy. + $arguments = array(); + foreach ($params['args'] as $paramargument) { + $arguments[$paramargument['name']] = $paramargument['value']; + } + + // The component was validated via the PARAM_COMPONENT parameter type. + $classname = '\\' . $params['component'] .'\output\mobile'; + if (!method_exists($classname, $params['method'])) { + throw new coding_exception("Missing method in $classname"); + } + $result = call_user_func_array(array($classname, $params['method']), array($arguments)); + + // Populate otherdata. + $otherdata = array(); + if (!empty($result['otherdata'])) { + $result['otherdata'] = (array) $result['otherdata']; + foreach ($result['otherdata'] as $name => $value) { + $otherdata[] = array( + 'name' => $name, + 'value' => $value + ); + } + } + + return array( + 'templates' => !empty($result['templates']) ? $result['templates'] : array(), + 'javascript' => !empty($result['javascript']) ? $result['javascript'] : '', + 'otherdata' => $otherdata, + 'files' => !empty($result['files']) ? $result['files'] : array(), + 'restrict' => !empty($result['restrict']) ? $result['restrict'] : array(), + ); + } + + /** + * Returns description of get_content() result value + * + * @return array + * @since Moodle 3.5 + */ + public static function get_content_returns() { + return new external_single_structure( + array( + 'templates' => new external_multiple_structure( + new external_single_structure( + array( + 'id' => new external_value(PARAM_TEXT, 'ID of the template.'), + 'html' => new external_value(PARAM_RAW, 'HTML code.'), + ) + ), + 'Templates required by the generated content.' + ), + 'javascript' => new external_value(PARAM_RAW, 'JavaScript code.'), + 'otherdata' => new external_multiple_structure( + new external_single_structure( + array( + 'name' => new external_value(PARAM_RAW, 'Field name.'), + 'value' => new external_value(PARAM_RAW, 'Field value.') + ) + ), + 'Other data that can be used or manipulated by the template via 2-way data-binding.' + ), + 'files' => new external_files('Files in the content.'), + 'restrict' => new external_single_structure( + array( + 'users' => new external_multiple_structure( + new external_value(PARAM_INT, 'user id'), 'List of allowed users.', VALUE_OPTIONAL + ), + 'courses' => new external_multiple_structure( + new external_value(PARAM_INT, 'course id'), 'List of allowed courses.', VALUE_OPTIONAL + ), + ), + 'Restrict this content to certain users or courses.' + ) + ) + ); + } } diff --git a/admin/tool/mobile/db/services.php b/admin/tool/mobile/db/services.php index aa64cc76cdf70..5e329c6ad868b 100644 --- a/admin/tool/mobile/db/services.php +++ b/admin/tool/mobile/db/services.php @@ -60,6 +60,13 @@ Is created only in https sites and is restricted by time and ip address.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), - ) + ), + 'tool_mobile_get_content' => array( + 'classname' => 'tool_mobile\external', + 'methodname' => 'get_content', + 'description' => 'Returns a piece of content to be displayed in the Mobile app.', + 'type' => 'read', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + ), ); diff --git a/admin/tool/mobile/tests/externallib_test.php b/admin/tool/mobile/tests/externallib_test.php index e61657c551592..0b8873bdcf1ff 100644 --- a/admin/tool/mobile/tests/externallib_test.php +++ b/admin/tool/mobile/tests/externallib_test.php @@ -29,6 +29,7 @@ global $CFG; require_once($CFG->dirroot . '/webservice/tests/helpers.php'); +require_once($CFG->dirroot . '/admin/tool/mobile/tests/fixtures/output/mobile.php'); use tool_mobile\external; use tool_mobile\api; @@ -305,4 +306,53 @@ public function test_get_autologin_key_missing_locked() { $this->expectExceptionMessage(get_string('autologinkeygenerationlockout', 'tool_mobile')); $result = external::get_autologin_key($token->privatetoken); } + + /** + * Test get_content. + */ + public function test_get_content() { + + $paramval = 16; + $result = external::get_content('tool_mobile', 'test_view', array(array('name' => 'param1', 'value' => $paramval))); + $result = external_api::clean_returnvalue(external::get_content_returns(), $result); + $this->assertCount(1, $result['templates']); + $this->assertCount(1, $result['otherdata']); + $this->assertCount(2, $result['restrict']['users']); + $this->assertCount(2, $result['restrict']['courses']); + $this->assertEquals('alert();', $result['javascript']); + $this->assertEquals('main', $result['templates'][0]['id']); + $this->assertEquals('The HTML code', $result['templates'][0]['html']); + $this->assertEquals('otherdata1', $result['otherdata'][0]['name']); + $this->assertEquals($paramval, $result['otherdata'][0]['value']); + $this->assertEquals(array(1, 2), $result['restrict']['users']); + $this->assertEquals(array(3, 4), $result['restrict']['courses']); + $this->assertEmpty($result['files']); + } + + /** + * Test get_content non existent function in valid component. + */ + public function test_get_content_non_existent_function() { + + $this->expectException('coding_exception'); + $result = external::get_content('tool_mobile', 'test_blahblah'); + } + + /** + * Test get_content incorrect component. + */ + public function test_get_content_invalid_component() { + + $this->expectException('moodle_exception'); + $result = external::get_content('tool_mobile\hack', 'test_view'); + } + + /** + * Test get_content non existent component. + */ + public function test_get_content_non_existent_component() { + + $this->expectException('moodle_exception'); + $result = external::get_content('tool_blahblahblah', 'test_view'); + } } diff --git a/admin/tool/mobile/tests/fixtures/output/mobile.php b/admin/tool/mobile/tests/fixtures/output/mobile.php new file mode 100644 index 0000000000000..d803743f35dee --- /dev/null +++ b/admin/tool/mobile/tests/fixtures/output/mobile.php @@ -0,0 +1,60 @@ +. + +/** + * Mock class for get_content. + * + * @package tool_mobile + * @copyright 2018 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_mobile\output; + +defined('MOODLE_INTERNAL') || die; + +/** + * Mock class for get_content. + * + * @package tool_mobile + * @copyright 2018 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mobile { + + /** + * Returns a test view. + * @param array $args Arguments from tool_mobile_get_content WS + * + * @return array HTML, javascript and otherdata + */ + public static function test_view($args) { + $args = (object) $args; + + return array( + 'templates' => array( + array( + 'id' => 'main', + 'html' => 'The HTML code', + ), + ), + 'javascript' => 'alert();', + 'otherdata' => array('otherdata1' => $args->param1), + 'restrict' => array('users' => array(1, 2), 'courses' => array(3, 4)), + 'files' => array() + ); + } +} diff --git a/admin/tool/mobile/version.php b/admin/tool/mobile/version.php index c09e6fd3b2ba7..438a6d5f68c1c 100644 --- a/admin/tool/mobile/version.php +++ b/admin/tool/mobile/version.php @@ -23,7 +23,7 @@ */ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX). +$plugin->version = 2017111301; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2017110800; // Requires this Moodle version. $plugin->component = 'tool_mobile'; // Full name of the plugin (used for diagnostics). $plugin->dependencies = array(