From d17877985140444465178c48d602071144c9b7ea Mon Sep 17 00:00:00 2001 From: Paul Holden <paulh@moodle.com> Date: Tue, 15 Sep 2020 00:33:07 +0100 Subject: [PATCH] MDL-69625 course: return raw custom field value in external method. For clients that wish to consume the original value of the custom field (e.g. timestamps for date fields), rather than the formatted version, add a new "valueraw" property to the returned data. --- course/externallib.php | 4 + course/tests/externallib_test.php | 150 +++++++++++++++++++++++++++++- course/upgrade.txt | 2 + 3 files changed, 152 insertions(+), 4 deletions(-) diff --git a/course/externallib.php b/course/externallib.php index ac64324eb4b..1f631b27a3b 100644 --- a/course/externallib.php +++ b/course/externallib.php @@ -611,6 +611,7 @@ class core_course_external extends external_api { $courseinfo['customfields'][] = [ 'type' => $data->get_type(), 'value' => $data->get_value(), + 'valueraw' => $data->get_data_controller()->get_value(), 'name' => $data->get_name(), 'shortname' => $data->get_shortname() ]; @@ -735,6 +736,7 @@ class core_course_external extends external_api { '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...'), + 'valueraw' => new external_value(PARAM_RAW, 'The raw value of the custom field'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field')] ), 'Custom fields and associated values', VALUE_OPTIONAL), ), 'course' @@ -2489,6 +2491,7 @@ class core_course_external extends external_api { $coursereturns['customfields'][] = [ 'type' => $data->get_type(), 'value' => $data->get_value(), + 'valueraw' => $data->get_data_controller()->get_value(), 'name' => $data->get_name(), 'shortname' => $data->get_shortname() ]; @@ -2640,6 +2643,7 @@ class core_course_external extends external_api { 'The shortname of the custom field - to be able to build the field class in the code'), 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'), + 'valueraw' => new external_value(PARAM_RAW, 'The raw value of the custom field'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field'), ) ), 'Custom fields', VALUE_OPTIONAL), diff --git a/course/tests/externallib_test.php b/course/tests/externallib_test.php index fcf938bc876..dcd48ee7cb6 100644 --- a/course/tests/externallib_test.php +++ b/course/tests/externallib_test.php @@ -775,8 +775,16 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { array('name' => 'coursedisplay', 'value' => $dbcourse->coursedisplay), )); } - if ($dbcourse->id == 4) { - $this->assertEquals($course['customfields'], [array_merge($customfield, $customfieldvalue)]); + + // Assert custom field that we previously added to test course 4. + if ($dbcourse->id == $course4->id) { + $this->assertEquals([ + 'shortname' => $customfield['shortname'], + 'name' => $customfield['name'], + 'type' => $customfield['type'], + 'value' => $customfieldvalue['value'], + 'valueraw' => $customfieldvalue['value'], + ], $course['customfields'][0]); } } @@ -789,6 +797,49 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { $this->assertEquals($DB->count_records('course'), count($courses)); } + /** + * Test retrieving courses returns custom field data + */ + public function test_get_courses_customfields(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $fieldcategory = $this->getDataGenerator()->create_custom_field_category([]); + $datefield = $this->getDataGenerator()->create_custom_field([ + 'categoryid' => $fieldcategory->get('id'), + 'shortname' => 'mydate', + 'name' => 'My date', + 'type' => 'date', + ]); + + $newcourse = $this->getDataGenerator()->create_course(['customfields' => [ + [ + 'shortname' => $datefield->get('shortname'), + 'value' => 1580389200, // 30/01/2020 13:00 GMT. + ], + ]]); + + $courses = external_api::clean_returnvalue( + core_course_external::get_courses_returns(), + core_course_external::get_courses(['ids' => [$newcourse->id]]) + ); + + $this->assertCount(1, $courses); + $course = reset($courses); + + $this->assertArrayHasKey('customfields', $course); + $this->assertCount(1, $course['customfields']); + + // Assert the received custom field, "value" containing a human-readable version and "valueraw" the unmodified version. + $this->assertEquals([ + 'name' => $datefield->get('name'), + 'shortname' => $datefield->get('shortname'), + 'type' => $datefield->get('type'), + 'value' => userdate(1580389200), + 'valueraw' => 1580389200, + ], reset($course['customfields'])); + } + /** * Test get_courses without capability */ @@ -912,6 +963,49 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { $results = core_course_external::search_courses('blocklist', $blockid); } + /** + * Test searching for courses returns custom field data + */ + public function test_search_courses_customfields(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $fieldcategory = $this->getDataGenerator()->create_custom_field_category([]); + $datefield = $this->getDataGenerator()->create_custom_field([ + 'categoryid' => $fieldcategory->get('id'), + 'shortname' => 'mydate', + 'name' => 'My date', + 'type' => 'date', + ]); + + $newcourse = $this->getDataGenerator()->create_course(['customfields' => [ + [ + 'shortname' => $datefield->get('shortname'), + 'value' => 1580389200, // 30/01/2020 13:00 GMT. + ], + ]]); + + $result = external_api::clean_returnvalue( + core_course_external::search_courses_returns(), + core_course_external::search_courses('search', $newcourse->shortname) + ); + + $this->assertCount(1, $result['courses']); + $course = reset($result['courses']); + + $this->assertArrayHasKey('customfields', $course); + $this->assertCount(1, $course['customfields']); + + // Assert the received custom field, "value" containing a human-readable version and "valueraw" the unmodified version. + $this->assertEquals([ + 'name' => $datefield->get('name'), + 'shortname' => $datefield->get('shortname'), + 'type' => $datefield->get('type'), + 'value' => userdate(1580389200), + 'valueraw' => 1580389200, + ], reset($course['customfields'])); + } + /** * Create a course with contents * @return array A list with the course object and course modules objects @@ -2453,8 +2547,13 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { $this->assertCount(1, $result['courses']); $this->assertEquals($course2->id, $result['courses'][0]['id']); // Check custom fields properly returned. - unset($customfield['categoryid']); - $this->assertEquals([array_merge($customfield, $customfieldvalue)], $result['courses'][0]['customfields']); + $this->assertEquals([ + 'shortname' => $customfield['shortname'], + 'name' => $customfield['name'], + 'type' => $customfield['type'], + 'value' => $customfieldvalue['value'], + 'valueraw' => $customfieldvalue['value'], + ], $result['courses'][0]['customfields'][0]); $result = core_course_external::get_courses_by_field('ids', "$course1->id,$course2->id"); $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); @@ -2583,6 +2682,49 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { $this->assertCount(0, $result['courses']); } + /** + * Test retrieving courses by field returns custom field data + */ + public function test_get_courses_by_field_customfields(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $fieldcategory = $this->getDataGenerator()->create_custom_field_category([]); + $datefield = $this->getDataGenerator()->create_custom_field([ + 'categoryid' => $fieldcategory->get('id'), + 'shortname' => 'mydate', + 'name' => 'My date', + 'type' => 'date', + ]); + + $newcourse = $this->getDataGenerator()->create_course(['customfields' => [ + [ + 'shortname' => $datefield->get('shortname'), + 'value' => 1580389200, // 30/01/2020 13:00 GMT. + ], + ]]); + + $result = external_api::clean_returnvalue( + core_course_external::get_courses_by_field_returns(), + core_course_external::get_courses_by_field('id', $newcourse->id) + ); + + $this->assertCount(1, $result['courses']); + $course = reset($result['courses']); + + $this->assertArrayHasKey('customfields', $course); + $this->assertCount(1, $course['customfields']); + + // Assert the received custom field, "value" containing a human-readable version and "valueraw" the unmodified version. + $this->assertEquals([ + 'name' => $datefield->get('name'), + 'shortname' => $datefield->get('shortname'), + 'type' => $datefield->get('type'), + 'value' => userdate(1580389200), + 'valueraw' => 1580389200, + ], reset($course['customfields'])); + } + public function test_get_courses_by_field_invalid_field() { $this->expectException('invalid_parameter_exception'); $result = core_course_external::get_courses_by_field('zyx', 'x'); diff --git a/course/upgrade.txt b/course/upgrade.txt index e56837d5b02..69bf9230ad4 100644 --- a/course/upgrade.txt +++ b/course/upgrade.txt @@ -5,6 +5,8 @@ information provided here is intended especially for developers. * The function make_categories_options() has now been deprecated. Please use \core_course_category::make_categories_list() instead. * External function core_course_external::get_course_contents now returns a new field contextid with the module context id. +* The core_course_external class methods get_courses(), get_courses_by_field() and search_courses() now return a "valueraw" property + for each custom course field, which contains the original/unformatted version of the custom field value. === 3.9 ===