MDL-56020 search: Adapt code to new style and add tests

This commit is contained in:
Juan Leyva 2023-04-19 15:00:50 +02:00
parent 49e589445d
commit f94e1ef3c9
9 changed files with 321 additions and 314 deletions

View file

@ -1707,12 +1707,11 @@ $functions = array(
'ajax' => true
),
'core_search_get_results' => array(
'classname' => 'core_search_external',
'methodname' => 'get_results',
'classpath' => 'search/classes/external.php',
'description' => 'Search contents.',
'classname' => '\core_search\external\get_results',
'description' => 'Get search results.',
'type' => 'read',
'capabilities' => 'moodle/search:query'
'capabilities' => 'moodle/search:query',
'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE],
),
'core_tag_get_tagindex' => array(
'classname' => 'core_tag_external',

View file

@ -392,18 +392,6 @@ class document implements \renderable, \templatable {
return static::$requiredfields + static::$optionalfields + static::$enginefields;
}
/**
* Returns whether a field is required or not.
*
* Considers engine fields to be optional.
*
* @param string $fieldname
* @return bool
*/
public static function field_is_required(string $fieldname): bool {
return (!empty(static::$requiredfields[$fieldname]));
}
/**
* Formats the timestamp preparing the time fields to be inserted into the search engine.
*
@ -618,10 +606,10 @@ class document implements \renderable, \templatable {
* Just delegates all the processing to export_doc_info, also used by external functions.
* Adding more info than the required one as people might be interested in extending the template.
*
* @param renderer_base $output The renderer.
* @param \renderer_base $output The renderer.
* @return array
*/
public function export_for_template(\renderer_base $output) {
public function export_for_template(\renderer_base $output): array {
$docdata = $this->export_doc($output);
return $docdata;
}
@ -637,10 +625,12 @@ class document implements \renderable, \templatable {
* SECURITY NOTE: It is the responsibility of the document to properly escape any text to be displayed.
* The renderer will output the content without any further cleaning.
*
* @param \renderer_base $output The renderer.
* @return array
*/
public function export_doc(\renderer_base $output) {
global $USER;
public function export_doc(\renderer_base $output): array {
global $USER, $CFG;
require_once($CFG->dirroot . '/course/lib.php');
list($componentname, $areaname) = \core_search\manager::extract_areaid_parts($this->get('areaid'));
$context = context::instance_by_id($this->get('contextid'));
@ -684,10 +674,10 @@ class document implements \renderable, \templatable {
if ($this->get('userid') == $USER->id ||
(has_capability('moodle/user:viewdetails', $context) &&
has_capability('moodle/course:viewparticipants', $context))) {
$data['userurl'] = new \moodle_url(
$data['userurl'] = (new \moodle_url(
'/user/view.php',
['id' => $this->get('userid'), 'course' => $this->get('courseid')]
);
))->out(false);
$data['userfullname'] = format_string($this->get('userfullname'), true, ['context' => $context->id]);
$data['userid'] = $this->get('userid');
}
@ -695,17 +685,10 @@ class document implements \renderable, \templatable {
if ($docicon = $this->get_doc_icon()) {
$data['icon'] = $output->image_url($docicon->get_name(), $docicon->get_component());
$data['iconurl'] = $data['icon']->out(false);
}
// We need to return the text formatting used for ws stuff.
$settings = \core_external\external_settings::get_instance();
if ($settings->get_raw()) {
// If this is called by a ws client and requests raw text we return the format specified by the search engine.
$data['textformat'] = $this->get_text_format();
} else {
// We convert texts to HTML by default.
$data['textformat'] = FORMAT_HTML;
}
return $data;
}

View file

@ -120,168 +120,4 @@ class external extends \core_external\external_api {
}
return $result;
}
/**
* get_results parameters.
*
* @since Moodle 3.2
* @return external_function_parameters
*/
public static function get_results_parameters() {
return new external_function_parameters(
array(
'q' => new external_value(PARAM_NOTAGS, 'the search query'),
'filters' => new external_single_structure(
array(
'title' => new external_value(PARAM_NOTAGS, 'result title', VALUE_OPTIONAL),
'areaids' => new external_multiple_structure(
new external_value(PARAM_RAW, 'areaid'), 'restrict results to these areas', VALUE_DEFAULT, []
),
'courseids' => new external_multiple_structure(
new external_value(PARAM_INT, 'courseid'), 'restrict results to these courses', VALUE_DEFAULT, []
),
'contextids' => new external_multiple_structure(
new external_value(PARAM_INT, 'contextid'), 'restrict results to these context', VALUE_DEFAULT, []
),
'userids' => new external_multiple_structure(
new external_value(PARAM_INT, 'userid'), 'restrict results to these users', VALUE_DEFAULT, []
),
'groupids' => new external_multiple_structure(
new external_value(PARAM_INT, 'groupid'), 'restrict results to these groups', VALUE_DEFAULT, []
),
'mycoursesonly' => new external_value(PARAM_BOOL, 'result title', VALUE_OPTIONAL),
'order' => new external_value(PARAM_ALPHA, 'result title', VALUE_OPTIONAL),
'timestart' => new external_value(PARAM_INT, 'result title', VALUE_DEFAULT, 0),
'timeend' => new external_value(PARAM_INT, 'result title', VALUE_DEFAULT, 0)
), 'filters to apply', VALUE_OPTIONAL
),
'page' => new external_value(PARAM_INT, 'results page number starting from 0, defaults to the first page',
VALUE_DEFAULT)
)
);
}
/*
* Gets global search results based on the provided query and filters.
*
* @param string $q
* @param array $filters
* @param int $page
* @return array
*/
public static function get_results($q, $filters = [], $page = 0) {
global $PAGE;
$params = self::validate_parameters(self::get_results_parameters(), array(
'q' => $q,
'filters' => $filters,
'page' => $page)
);
$system = \context_system::instance();
\external_api::validate_context($system);
require_capability('moodle/search:query', $system);
if (\core_search\manager::is_global_search_enabled() === false) {
throw new \moodle_exception('globalsearchdisabled', 'search');
}
$search = \core_search\manager::instance();
$data = new \stdClass();
$data->q = $params['q'];
if (!empty($params['filters']['title'])) {
$data->title = $params['filters']['title'];
}
if (!empty($params['filters']['areaids'])) {
$data->areaids = $params['filters']['areaids'];
}
if (!empty($params['filters']['courseids'])) {
$data->courseids = $params['filters']['courseids'];
}
if (!empty($params['filters']['contextids'])) {
$data->contextids = $params['filters']['contextids'];
}
if (!empty($params['filters']['userids'])) {
$data->userids = $params['filters']['userids'];
}
if (!empty($params['filters']['groupids'])) {
$data->groupids = $params['filters']['groupids'];
}
if (!empty($params['filters']['timestart'])) {
$data->timestart = $params['filters']['timestart'];
}
if (!empty($params['filters']['timeend'])) {
$data->timeend = $params['filters']['timeend'];
}
$docs = $search->paged_search($data, $page);
$return = [
'totalcount' => $docs->totalcount,
'warnings' => [],
'results' => []
];
// Convert results to simple data structures.
if ($docs) {
foreach ($docs->results as $doc) {
$return['results'][] = $doc->export_doc($PAGE->get_renderer('core'));
}
}
return $return;
}
/**
* Returns description of method get_results.
*
* @return external_single_structure
*/
public static function get_results_returns() {
return new external_single_structure(
array(
'totalcount' => new external_value(PARAM_INT, 'Total number of results'),
'results' => new external_multiple_structure(
new external_single_structure(
array(
'itemid' => new external_value(PARAM_INT, 'unique id in the search area scope'),
'componentname' => new external_value(PARAM_ALPHANUMEXT, 'component name'),
'areaname' => new external_value(PARAM_ALPHANUMEXT, 'search area name'),
'courseurl' => new external_value(PARAM_URL, 'result course url'),
'coursefullname' => new external_value(PARAM_RAW, 'result course fullname'),
'timemodified' => new external_value(PARAM_INT, 'result modified time'),
'title' => new external_value(PARAM_RAW, 'result title'),
'docurl' => new external_value(PARAM_URL, 'result url'),
'content' => new external_value(PARAM_RAW, 'result contents', VALUE_OPTIONAL),
'contextid' => new external_value(PARAM_INT, 'result context id'),
'contexturl' => new external_value(PARAM_URL, 'result context url'),
'description1' => new external_value(PARAM_RAW, 'extra result contents, depends on the search area', VALUE_OPTIONAL),
'description2' => new external_value(PARAM_RAW, 'extra result contents, depends on the search area', VALUE_OPTIONAL),
'multiplefiles' => new external_value(PARAM_INT, 'whether multiple files are returned or not', VALUE_OPTIONAL),
'filenames' => new external_multiple_structure(
new external_value(PARAM_RAW, 'result file name', VALUE_OPTIONAL)
, 'result file names if present',
VALUE_OPTIONAL
),
'filename' => new external_value(PARAM_RAW, 'result file name if present', VALUE_OPTIONAL),
'userid' => new external_value(PARAM_INT, 'user id', VALUE_OPTIONAL),
'userurl' => new external_value(PARAM_URL, 'user url', VALUE_OPTIONAL),
'userfullname' => new external_value(PARAM_RAW, 'user fullname', VALUE_OPTIONAL),
'textformat' => new external_value(PARAM_INT, 'text fields format, it is the same for all of them')
), 'Search result'
), 'Search results', VALUE_OPTIONAL
)
)
);
}
}

189
search/classes/external/get_results.php vendored Normal file
View file

@ -0,0 +1,189 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_search\external;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_multiple_structure;
use core_external\external_value;
use moodle_exception;
/**
* External function for retrieving search results.
*
* @package core_search
* @copyright 2023 David Monllao & Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.3
*/
class get_results extends external_api {
/**
* Webservice parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters(
[
'query' => new external_value(PARAM_NOTAGS, 'the search query'),
'filters' => new external_single_structure(
[
'title' => new external_value(PARAM_NOTAGS, 'result title', VALUE_OPTIONAL),
'areaids' => new external_multiple_structure(
new external_value(PARAM_ALPHANUMEXT, 'areaid'), 'restrict results to these areas', VALUE_DEFAULT, []
),
'courseids' => new external_multiple_structure(
new external_value(PARAM_INT, 'courseid'), 'restrict results to these courses', VALUE_DEFAULT, []
),
'contextids' => new external_multiple_structure(
new external_value(PARAM_INT, 'contextid'), 'restrict results to these contexts', VALUE_DEFAULT, []
),
'cat' => new external_value(PARAM_NOTAGS, 'category to filter areas', VALUE_DEFAULT, ''),
'userids' => new external_multiple_structure(
new external_value(PARAM_INT, 'userid'), 'restrict results to these users', VALUE_DEFAULT, []
),
'groupids' => new external_multiple_structure(
new external_value(PARAM_INT, 'groupid'), 'restrict results to these groups', VALUE_DEFAULT, []
),
'mycoursesonly' => new external_value(PARAM_BOOL, 'only results from my courses', VALUE_DEFAULT, false),
'order' => new external_value(PARAM_ALPHA, 'how to order', VALUE_DEFAULT, ''),
'timestart' => new external_value(PARAM_INT, 'docs modified after this date', VALUE_DEFAULT, 0),
'timeend' => new external_value(PARAM_INT, 'docs modified before this date', VALUE_DEFAULT, 0)
], 'filters to apply', VALUE_DEFAULT, []
),
'page' => new external_value(PARAM_INT, 'results page number starting from 0, defaults to the first page',
VALUE_DEFAULT, 0)
]
);
}
/**
* Gets global search results based on the provided query and filters.
*
* @param string $query the search query
* @param array $filters filters to apply
* @param int $page results page
* @return array search results
*/
public static function execute(string $query, array $filters = [], int $page = 0): array {
global $PAGE;
$params = self::validate_parameters(self::execute_parameters(),
[
'query' => $query,
'filters' => $filters,
'page' => $page,
]
);
$system = \context_system::instance();
external_api::validate_context($system);
require_capability('moodle/search:query', $system);
if (\core_search\manager::is_global_search_enabled() === false) {
throw new moodle_exception('globalsearchdisabled', 'search');
}
$search = \core_search\manager::instance();
$data = new \stdClass();
// First, mandatory parameters for consistency with web.
$data->q = $params['query'];
$data->title = $params['filters']['title'] ?? '';
$data->timestart = $params['filters']['timestart'] ?? 0;
$data->timeend = $params['filters']['timeend'] ?? 0;
$data->areaids = $params['filters']['areaids'] ?? [];
$data->courseids = $params['filters']['courseids'] ?? [];
$data->contextids = $params['filters']['contextids'] ?? [];
$data->userids = $params['filters']['userids'] ?? [];
$data->groupids = $params['filters']['groupids'] ?? [];
$cat = $params['filters']['cat'] ?? '';
if (\core_search\manager::is_search_area_categories_enabled()) {
$cat = \core_search\manager::get_search_area_category_by_name($cat);
}
if ($cat instanceof \core_search\area_category) {
$data->cat = $cat->get_name();
}
$docs = $search->paged_search($data, $page);
$return = [
'totalcount' => $docs->totalcount,
'warnings' => [],
'results' => []
];
// Convert results to simple data structures.
if ($docs) {
foreach ($docs->results as $doc) {
$return['results'][] = $doc->export_doc($PAGE->get_renderer('core'));
}
}
return $return;
}
/**
* Webservice returns.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure(
[
'totalcount' => new external_value(PARAM_INT, 'Total number of results'),
'results' => new external_multiple_structure(
new external_single_structure(
[
'itemid' => new external_value(PARAM_INT, 'unique id in the search area scope'),
'componentname' => new external_value(PARAM_ALPHANUMEXT, 'component name'),
'areaname' => new external_value(PARAM_ALPHANUMEXT, 'search area name'),
'courseurl' => new external_value(PARAM_URL, 'result course url'),
'coursefullname' => new external_value(PARAM_RAW, 'result course fullname'),
'timemodified' => new external_value(PARAM_INT, 'result modified time'),
'title' => new external_value(PARAM_RAW, 'result title'),
'docurl' => new external_value(PARAM_URL, 'result url'),
'iconurl' => new external_value(PARAM_URL, 'icon url', VALUE_OPTIONAL),
'content' => new external_value(PARAM_RAW, 'result contents', VALUE_OPTIONAL),
'contextid' => new external_value(PARAM_INT, 'result context id'),
'contexturl' => new external_value(PARAM_URL, 'result context url'),
'description1' => new external_value(PARAM_RAW, 'extra result contents, depends on the search area',
VALUE_OPTIONAL),
'description2' => new external_value(PARAM_RAW, 'extra result contents, depends on the search area',
VALUE_OPTIONAL),
'multiplefiles' => new external_value(PARAM_INT, 'whether multiple files are returned or not',
VALUE_OPTIONAL),
'filenames' => new external_multiple_structure(
new external_value(PARAM_RAW, 'result file name', VALUE_OPTIONAL)
, 'result file names if present',
VALUE_OPTIONAL
),
'filename' => new external_value(PARAM_RAW, 'result file name if present', VALUE_OPTIONAL),
'userid' => new external_value(PARAM_INT, 'user id', VALUE_OPTIONAL),
'userurl' => new external_value(PARAM_URL, 'user url', VALUE_OPTIONAL),
'userfullname' => new external_value(PARAM_RAW, 'user fullname', VALUE_OPTIONAL),
'textformat' => new external_value(PARAM_INT, 'text fields format, it is the same for all of them')
], 'Search result'
), 'Search results', VALUE_OPTIONAL
)
]
);
}
}

View file

@ -323,7 +323,7 @@ class engine extends \core_search\engine {
* @return int
*/
public function get_query_total_count() {
if (!is_null($this->totalresults)) {
if (is_null($this->totalresults)) {
// This is a just in case as we count total results in execute_query.
return \core_search\manager::MAX_RESULTS;
}

View file

@ -0,0 +1,115 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_search\external;
use core_external\external_api;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
/**
* Tests for the get_results external function.
*
* @package core_search
* @category test
* @copyright 2023 Juan Leyva (juan@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core_search\external\get_results
*/
class get_results_test extends \externallib_advanced_testcase {
public function setUp(): void {
$this->resetAfterTest();
}
/**
* test external api
* @covers ::execute
* @return void
*/
public function test_external_get_results(): void {
set_config('enableglobalsearch', true);
set_config('searchengine', 'simpledb');
$this->setAdminUser();
// Test search not returning anything (nothing in the index yet).
$return = external_api::clean_returnvalue(get_results::execute_returns(), get_results::execute('one'));
$this->assertEquals(0, $return['totalcount']);
// Create an index of searchable things.
$generator = $this->getDataGenerator();
$course = $generator->create_course(['fullname' => 'SearchTest course']);
$anothercourse = $generator->create_course(['fullname' => 'Another']);
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$studentothercourse = $this->getDataGenerator()->create_and_enrol($anothercourse, 'student');
$page = $generator->create_module('page', ['course' => $course->id, 'name' => 'SearchTest page']);
$forum = $generator->create_module('forum', ['course' => $course->id]);
$fgenerator = $generator->get_plugin_generator('mod_forum');
for ($i = 0; $i < 15; $i++) {
$fgenerator->create_discussion(
[
'course' => $course->id,
'forum' => $forum->id,
'userid' => $student->id,
]
);
}
$search = \core_search\manager::instance();
$search->index();
// Basic search, by text.
$return = external_api::clean_returnvalue(get_results::execute_returns(), get_results::execute('page'));
$this->assertEquals(1, $return['totalcount']);
$this->assertEquals('activity', $return['results'][0]['areaname']);
$this->assertEquals($page->name, $return['results'][0]['title']);
// Basic search, by name containing text.
$return = external_api::clean_returnvalue(get_results::execute_returns(), get_results::execute('SearchTest'));
$this->assertEquals(2, $return['totalcount']);
// Test pagination.
$return = external_api::clean_returnvalue(get_results::execute_returns(), get_results::execute('discussion', [], 0));
$this->assertCount(10, $return['results']); // The first 10 posts of a total of 15 for the second page.
$this->assertEquals(15, $return['totalcount']);
$return = external_api::clean_returnvalue(get_results::execute_returns(), get_results::execute('discussion', [], 1));
$this->assertCount(5, $return['results']); // The last 5 posts of a total of 15 for the second page.
$this->assertEquals(15, $return['totalcount']);
// Test some filters.
$return = external_api::clean_returnvalue(get_results::execute_returns(),
get_results::execute('discussion', ['title' => 'Discussion 11']));
$this->assertEquals(1, $return['totalcount']);
// No discussions created in the future.
$return = external_api::clean_returnvalue(get_results::execute_returns(),
get_results::execute('discussion', ['timestart' => time() + DAYSECS]));
$this->assertEquals(0, $return['totalcount']);
// Basic permissions check.
$this->setUser($studentothercourse);
$return = external_api::clean_returnvalue(get_results::execute_returns(), get_results::execute('discussion', [], 1));
$this->assertCount(0, $return['results']); // I should not see other courses discussions.
}
}

View file

@ -75,119 +75,4 @@ class external_test extends \advanced_testcase {
// Note: We are not checking search permissions, search by different fields, etc. as these
// are covered by the core_user::search unit test.
}
/**
* test external api
*
* @return void
*/
public function test_external_get_results() {
global $USER, $DB, $CFG;
require_once($CFG->dirroot . '/lib/externallib.php');
set_config('enableglobalsearch', true);
set_config('searchengine', 'simpledb');
$course = $this->getDataGenerator()->create_course();
$this->setAdminUser();
// Filters with defaults.
$filters = array(
'title' => null,
'areaids' => array(),
'courseids' => array(),
'timestart' => 0,
'timeend' => 0
);
// We need to execute the return values cleaning process to simulate the web service server.
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results('one', $filters));
$this->assertEquals(0, $return['totalcount']);
$search = \core_search\manager::instance();
$search->index();
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results($course->shortname));
print_r($return);
$this->assertEquals(0, $return['totalcount']);
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results('message', $filters));
$this->assertEquals(2, $return['totalcount']);
$this->assertEquals($USER->id, $return['results'][0]['userid']);
$this->assertEquals(\context_system::instance()->id, $return['results'][0]['contextid']);
sleep(1);
$beforeadding = time();
sleep(1);
$this->generator->create_record();
$this->search->index();
// Timestart.
$filters['timestart'] = $beforeadding;
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results('message', $filters));
$this->assertEquals(1, $return['totalcount']);
// Timeend.
$filters['timestart'] = 0;
$filters['timeend'] = $beforeadding;
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results('message', $filters));
$this->assertEquals(2, $return['totalcount']);
// Title.
$filters['timeend'] = 0;
$filters['title'] = 'Special title';
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results('message', $filters));
$this->assertEquals(1, $return['totalcount']);
// Course IDs.
$filters['title'] = null;
$filters['courseids'] = array(SITEID + 1);
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results('message', $filters));
$this->assertEquals(0, $return['totalcount']);
$filters['courseids'] = array(SITEID);
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results('message', $filters));
$this->assertEquals(3, $return['totalcount']);
// Reset filters once again.
$filters['courseids'] = array();
// Now try some area-id combinations.
$forumpostareaid = \core_search\manager::generate_areaid('mod_forum', 'post');
$mockareaid = \core_search\manager::generate_areaid('core_mocksearch', 'mock_search_area');
$filters['areaids'] = array($forumpostareaid);
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results('message', $filters));
$this->assertEquals(0, $return['totalcount']);
$filters['areaids'] = array($forumpostareaid, $mockareaid);
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results('message', $filters));
$this->assertEquals(3, $return['totalcount']);
$filters['areaids'] = array($mockareaid);
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results('message', $filters));
$this->assertEquals(3, $return['totalcount']);
// All records now.
$filters['areaids'] = array();
$return = \external_api::clean_returnvalue(\core_search\external::get_results_returns(),
\core_search\external::get_results('message', $filters));
$this->assertEquals(3, $return['totalcount']);
}
}

View file

@ -61,7 +61,7 @@ class engine extends \core_search\engine {
public function execute_query($data, $usercontexts, $limit = 0) {
// No need to implement.
return array();
return [];
}
public function delete($areaid = null) {

View file

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2023082600.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2023082600.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.3dev+ (Build: 20230826)'; // Human-friendly version name