mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 08:26:37 +02:00
MDL-56020 search: Adapt code to new style and add tests
This commit is contained in:
parent
49e589445d
commit
f94e1ef3c9
9 changed files with 321 additions and 314 deletions
|
@ -1707,12 +1707,11 @@ $functions = array(
|
||||||
'ajax' => true
|
'ajax' => true
|
||||||
),
|
),
|
||||||
'core_search_get_results' => array(
|
'core_search_get_results' => array(
|
||||||
'classname' => 'core_search_external',
|
'classname' => '\core_search\external\get_results',
|
||||||
'methodname' => 'get_results',
|
'description' => 'Get search results.',
|
||||||
'classpath' => 'search/classes/external.php',
|
|
||||||
'description' => 'Search contents.',
|
|
||||||
'type' => 'read',
|
'type' => 'read',
|
||||||
'capabilities' => 'moodle/search:query'
|
'capabilities' => 'moodle/search:query',
|
||||||
|
'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE],
|
||||||
),
|
),
|
||||||
'core_tag_get_tagindex' => array(
|
'core_tag_get_tagindex' => array(
|
||||||
'classname' => 'core_tag_external',
|
'classname' => 'core_tag_external',
|
||||||
|
|
|
@ -392,18 +392,6 @@ class document implements \renderable, \templatable {
|
||||||
return static::$requiredfields + static::$optionalfields + static::$enginefields;
|
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.
|
* 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.
|
* 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.
|
* 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
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function export_for_template(\renderer_base $output) {
|
public function export_for_template(\renderer_base $output): array {
|
||||||
$docdata = $this->export_doc($output);
|
$docdata = $this->export_doc($output);
|
||||||
return $docdata;
|
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.
|
* 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.
|
* The renderer will output the content without any further cleaning.
|
||||||
*
|
*
|
||||||
|
* @param \renderer_base $output The renderer.
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function export_doc(\renderer_base $output) {
|
public function export_doc(\renderer_base $output): array {
|
||||||
global $USER;
|
global $USER, $CFG;
|
||||||
|
require_once($CFG->dirroot . '/course/lib.php');
|
||||||
|
|
||||||
list($componentname, $areaname) = \core_search\manager::extract_areaid_parts($this->get('areaid'));
|
list($componentname, $areaname) = \core_search\manager::extract_areaid_parts($this->get('areaid'));
|
||||||
$context = context::instance_by_id($this->get('contextid'));
|
$context = context::instance_by_id($this->get('contextid'));
|
||||||
|
@ -684,10 +674,10 @@ class document implements \renderable, \templatable {
|
||||||
if ($this->get('userid') == $USER->id ||
|
if ($this->get('userid') == $USER->id ||
|
||||||
(has_capability('moodle/user:viewdetails', $context) &&
|
(has_capability('moodle/user:viewdetails', $context) &&
|
||||||
has_capability('moodle/course:viewparticipants', $context))) {
|
has_capability('moodle/course:viewparticipants', $context))) {
|
||||||
$data['userurl'] = new \moodle_url(
|
$data['userurl'] = (new \moodle_url(
|
||||||
'/user/view.php',
|
'/user/view.php',
|
||||||
['id' => $this->get('userid'), 'course' => $this->get('courseid')]
|
['id' => $this->get('userid'), 'course' => $this->get('courseid')]
|
||||||
);
|
))->out(false);
|
||||||
$data['userfullname'] = format_string($this->get('userfullname'), true, ['context' => $context->id]);
|
$data['userfullname'] = format_string($this->get('userfullname'), true, ['context' => $context->id]);
|
||||||
$data['userid'] = $this->get('userid');
|
$data['userid'] = $this->get('userid');
|
||||||
}
|
}
|
||||||
|
@ -695,17 +685,10 @@ class document implements \renderable, \templatable {
|
||||||
|
|
||||||
if ($docicon = $this->get_doc_icon()) {
|
if ($docicon = $this->get_doc_icon()) {
|
||||||
$data['icon'] = $output->image_url($docicon->get_name(), $docicon->get_component());
|
$data['icon'] = $output->image_url($docicon->get_name(), $docicon->get_component());
|
||||||
|
$data['iconurl'] = $data['icon']->out(false);
|
||||||
}
|
}
|
||||||
|
$data['textformat'] = $this->get_text_format();
|
||||||
|
|
||||||
// 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;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,168 +120,4 @@ class external extends \core_external\external_api {
|
||||||
}
|
}
|
||||||
return $result;
|
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
189
search/classes/external/get_results.php
vendored
Normal 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
|
||||||
|
)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -323,7 +323,7 @@ class engine extends \core_search\engine {
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function get_query_total_count() {
|
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.
|
// This is a just in case as we count total results in execute_query.
|
||||||
return \core_search\manager::MAX_RESULTS;
|
return \core_search\manager::MAX_RESULTS;
|
||||||
}
|
}
|
||||||
|
|
115
search/tests/external/get_results_test.php
vendored
Normal file
115
search/tests/external/get_results_test.php
vendored
Normal 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.
|
||||||
|
}
|
||||||
|
}
|
|
@ -75,119 +75,4 @@ class external_test extends \advanced_testcase {
|
||||||
// Note: We are not checking search permissions, search by different fields, etc. as these
|
// Note: We are not checking search permissions, search by different fields, etc. as these
|
||||||
// are covered by the core_user::search unit test.
|
// 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']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
2
search/tests/fixtures/mock_search_engine.php
vendored
2
search/tests/fixtures/mock_search_engine.php
vendored
|
@ -61,7 +61,7 @@ class engine extends \core_search\engine {
|
||||||
|
|
||||||
public function execute_query($data, $usercontexts, $limit = 0) {
|
public function execute_query($data, $usercontexts, $limit = 0) {
|
||||||
// No need to implement.
|
// No need to implement.
|
||||||
return array();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete($areaid = null) {
|
public function delete($areaid = null) {
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
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.
|
// RR = release increments - 00 in DEV branches.
|
||||||
// .XX = incremental changes.
|
// .XX = incremental changes.
|
||||||
$release = '4.3dev+ (Build: 20230826)'; // Human-friendly version name
|
$release = '4.3dev+ (Build: 20230826)'; // Human-friendly version name
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue