diff --git a/admin/searchareas.php b/admin/searchareas.php index 911a86bf94b..fb5b0a30918 100644 --- a/admin/searchareas.php +++ b/admin/searchareas.php @@ -153,6 +153,7 @@ $table = new html_table(); $table->id = 'core-search-areas'; $table->head = [ get_string('searcharea', 'search'), + get_string('searchareacategories', 'search'), get_string('enable'), get_string('newestdocindexed', 'admin'), get_string('searchlastrun', 'admin'), @@ -165,6 +166,14 @@ foreach ($searchareas as $area) { $areaid = $area->get_area_id(); $columns = array(new html_table_cell($area->get_visible_name())); + $areacategories = []; + foreach (\core_search\manager::get_search_area_categories() as $category) { + if (key_exists($areaid, $category->get_areas())) { + $areacategories[] = $category->get_visiblename(); + } + } + $columns[] = new html_table_cell(implode(', ', $areacategories)); + if ($area->is_enabled()) { $columns[] = $OUTPUT->action_icon(admin_searcharea_action_url('disable', $areaid), new pix_icon('t/hide', get_string('disable'), 'moodle', array('title' => '', 'class' => 'iconsmall')), diff --git a/admin/settings/plugins.php b/admin/settings/plugins.php index 9a2202f158c..94399c74cb0 100644 --- a/admin/settings/plugins.php +++ b/admin/settings/plugins.php @@ -573,6 +573,25 @@ if ($hassiteconfig) { new lang_string('searchallavailablecourses_desc', 'admin'), 0, $options)); + // Search display options. + $temp->add(new admin_setting_heading('searchdisplay', new lang_string('searchdisplay', 'admin'), '')); + $temp->add(new admin_setting_configcheckbox('searchenablecategories', + new lang_string('searchenablecategories', 'admin'), + new lang_string('searchenablecategories_desc', 'admin'), + 0)); + $options = []; + foreach (\core_search\manager::get_search_area_categories() as $category) { + $options[$category->get_name()] = $category->get_visiblename(); + } + $temp->add(new admin_setting_configselect('searchdefaultcategory', + new lang_string('searchdefaultcategory', 'admin'), + new lang_string('searchdefaultcategory_desc', 'admin'), + \core_search\manager::SEARCH_AREA_CATEGORY_ALL, $options)); + $temp->add(new admin_setting_configcheckbox('searchhideallcategory', + new lang_string('searchhideallcategory', 'admin'), + new lang_string('searchhideallcategory_desc', 'admin'), + 0)); + $ADMIN->add('searchplugins', $temp); $ADMIN->add('searchplugins', new admin_externalpage('searchareas', new lang_string('searchareas', 'admin'), new moodle_url('/admin/searchareas.php'))); diff --git a/course/classes/search/mycourse.php b/course/classes/search/mycourse.php index 9cbdc58e679..bac012796fc 100644 --- a/course/classes/search/mycourse.php +++ b/course/classes/search/mycourse.php @@ -184,4 +184,13 @@ class mycourse extends \core_search\base { public function get_doc_icon(\core_search\document $doc) : \core_search\document_icon { return new \core_search\document_icon('i/course'); } + + /** + * Returns a list of category names associated with the area. + * + * @return array + */ + public function get_category_names() { + return [\core_search\manager::SEARCH_AREA_CATEGORY_COURSES]; + } } diff --git a/course/classes/search/section.php b/course/classes/search/section.php index 42badb486a9..6be829d95b0 100644 --- a/course/classes/search/section.php +++ b/course/classes/search/section.php @@ -205,4 +205,13 @@ class section extends \core_search\base { public function get_doc_icon(\core_search\document $doc) : \core_search\document_icon { return new \core_search\document_icon('i/section'); } + + /** + * Returns a list of category names associated with the area. + * + * @return array + */ + public function get_category_names() { + return [\core_search\manager::SEARCH_AREA_CATEGORY_COURSE_CONTENT]; + } } diff --git a/course/tests/search_test.php b/course/tests/search_test.php index ac9d44d7a7f..cc3e9be2721 100644 --- a/course/tests/search_test.php +++ b/course/tests/search_test.php @@ -478,4 +478,15 @@ class course_search_testcase extends advanced_testcase { $this->assertEquals('i/section', $result->get_name()); $this->assertEquals('moodle', $result->get_component()); } + + /** + * Test assigned search categories. + */ + public function test_get_category_names() { + $coursessearcharea = \core_search\manager::get_search_area($this->mycoursesareaid); + $sectionsearcharea = \core_search\manager::get_search_area($this->sectionareaid); + + $this->assertEquals(['core-courses'], $coursessearcharea->get_category_names()); + $this->assertEquals(['core-course-content'], $sectionsearcharea->get_category_names()); + } } diff --git a/lang/en/admin.php b/lang/en/admin.php index dbb7577c9c7..ddb32c6c2af 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -1054,6 +1054,13 @@ $string['searchallavailablecourses'] = 'Searchable courses'; $string['searchallavailablecourses_off'] = 'Search within enrolled courses only'; $string['searchallavailablecourses_on'] = 'Search within all courses the user can access'; $string['searchallavailablecourses_desc'] = 'In some situations the search engine may not work when searching across a large number of courses. Set to search only enrolled courses if you need to restrict the number of courses searched.'; +$string['searchdisplay'] = 'Search results display options'; +$string['searchenablecategories'] = 'Display results in separate categories'; +$string['searchenablecategories_desc'] = 'If enabled, search results will be displayed in separate categories.'; +$string['searchhideallcategory'] = 'Hide All results category'; +$string['searchhideallcategory_desc'] = 'If checked, the category with all results will be hidden on the search result screen.'; +$string['searchdefaultcategory'] = 'Default search category'; +$string['searchdefaultcategory_desc'] = 'Results from the selected search area category will be displayed by default.'; $string['searchalldeleted'] = 'All indexed contents have been deleted'; $string['searchareaenabled'] = 'Search area enabled'; $string['searchareadisabled'] = 'Search area disabled'; diff --git a/lang/en/search.php b/lang/en/search.php index e9560989dc6..3623b3f9116 100644 --- a/lang/en/search.php +++ b/lang/en/search.php @@ -36,6 +36,11 @@ $string['confirm_delete'] = 'Are you sure you want to delete the index for {$a}? $string['confirm_indexall'] = 'Are you sure you want to update indexed contents now? If a large amount of content needs indexing, this can take a long time. For live servers, you should normally leave indexing to the \'Global search indexing\' scheduled task.'; $string['confirm_reindexall'] = 'Are you sure you want to reindex all site contents now? If your site contains a large amount of content, this will take a long time, and users may not get full search results until it completes.'; $string['confirm_deleteall'] = 'Are you sure you want to delete all indexed contents now? Until the site is indexed again, users will not get search results.'; +$string['core-all'] = 'All'; +$string['core-course-content'] = 'Course content'; +$string['core-courses'] = 'Courses'; +$string['core-users'] = 'Users'; +$string['core-other'] = 'Other'; $string['createanindex'] = 'create an index'; $string['createdon'] = 'Created on'; $string['database'] = 'Database'; @@ -111,6 +116,7 @@ $string['search:mycourse'] = 'My courses'; $string['search:section'] = 'Course sections'; $string['search:user'] = 'Users'; $string['searcharea'] = 'Search area'; +$string['searchareacategories'] = 'Seach area categories'; $string['searching'] = 'Searching in ...'; $string['searchnotpermitted'] = 'You are not allowed to do a search'; $string['searchsetupdescription'] = 'The following steps help you to set up Moodle global search.'; diff --git a/message/classes/search/base_message.php b/message/classes/search/base_message.php index 5a3912f0be6..a4aa6fe125d 100644 --- a/message/classes/search/base_message.php +++ b/message/classes/search/base_message.php @@ -207,4 +207,13 @@ abstract class base_message extends \core_search\base { public function get_doc_icon(\core_search\document $doc) : \core_search\document_icon { return new \core_search\document_icon('t/message'); } + + /** + * Returns a list of category names associated with the area. + * + * @return array + */ + public function get_category_names() { + return [\core_search\manager::SEARCH_AREA_CATEGORY_USERS]; + } } diff --git a/message/tests/search_received_test.php b/message/tests/search_received_test.php index 79b7137ae38..c8f53ac366e 100644 --- a/message/tests/search_received_test.php +++ b/message/tests/search_received_test.php @@ -352,4 +352,14 @@ class message_received_search_testcase extends advanced_testcase { $this->assertEquals('t/message', $result->get_name()); $this->assertEquals('moodle', $result->get_component()); } + + /** + * Test assigned search categories. + */ + public function test_get_category_names() { + $searcharea = \core_search\manager::get_search_area($this->messagereceivedareaid); + + $expected = ['core-users']; + $this->assertEquals($expected, $searcharea->get_category_names()); + } } diff --git a/message/tests/search_sent_test.php b/message/tests/search_sent_test.php index 7edee7d6d8a..eb42216cb2e 100644 --- a/message/tests/search_sent_test.php +++ b/message/tests/search_sent_test.php @@ -367,4 +367,14 @@ class message_sent_search_testcase extends advanced_testcase { $this->assertEquals('t/message', $result->get_name()); $this->assertEquals('moodle', $result->get_component()); } + + /** + * Test assigned search categories. + */ + public function test_get_category_names() { + $searcharea = \core_search\manager::get_search_area($this->messagesentareaid); + + $expected = ['core-users']; + $this->assertEquals($expected, $searcharea->get_category_names()); + } } diff --git a/search/classes/area_category.php b/search/classes/area_category.php new file mode 100644 index 00000000000..975ed61908b --- /dev/null +++ b/search/classes/area_category.php @@ -0,0 +1,126 @@ +. + +/** + * Search area category. + * + * @package core_search + * @copyright Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_search; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Search area category. + * + * @package core_search + * @copyright Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class area_category { + + /** + * Category name. + * @var string + */ + protected $name; + + /** + * Category visible name. + * @var string + */ + protected $visiblename; + + /** + * Category order. + * @var int + */ + protected $order = 0; + + /** + * Category areas. + * @var \core_search\base[] + */ + protected $areas = []; + + /** + * Constructor. + * + * @param string $name Unique name of the category. + * @param string $visiblename Visible name of the category. + * @param int $order Category position in the list (smaller numbers will be displayed first). + * @param \core_search\base[] $areas A list of search areas associated with this category. + */ + public function __construct(string $name, string $visiblename, int $order = 0, array $areas = []) { + $this->name = $name; + $this->visiblename = $visiblename; + $this->order = $order; + $this->set_areas($areas); + } + + /** + * Get name. + * + * @return string + */ + public function get_name() { + return $this->name; + } + + /** + * Get visible name. + * + * @return string + */ + public function get_visiblename() { + return $this->visiblename; + } + + /** + * Get order to display. + * + * @return int + */ + public function get_order() { + return $this->order; + } + + /** + * Return a keyed by area id list of areas for this category. + * + * @return \core_search\base[] + */ + public function get_areas() { + return $this->areas; + } + + /** + * Set list of search areas for this category, + * + * @param \core_search\base[] $areas + */ + public function set_areas(array $areas) { + foreach ($areas as $area) { + if ($area instanceof base && !key_exists($area->get_area_id(), $this->areas)) { + $this->areas[$area->get_area_id()] = $area; + } + } + } + +} diff --git a/search/classes/base.php b/search/classes/base.php index ff1006fae3d..c2315486c3e 100644 --- a/search/classes/base.php +++ b/search/classes/base.php @@ -532,4 +532,13 @@ abstract class base { public function get_doc_icon(document $doc) : document_icon { return new document_icon('i/empty'); } + + /** + * Returns a list of category names associated with the area. + * + * @return array + */ + public function get_category_names() { + return [manager::SEARCH_AREA_CATEGORY_OTHER]; + } } diff --git a/search/classes/base_block.php b/search/classes/base_block.php index 6f4bdc8a2ea..28e7b107906 100644 --- a/search/classes/base_block.php +++ b/search/classes/base_block.php @@ -408,4 +408,13 @@ abstract class base_block extends base { public function get_doc_icon(document $doc) : document_icon { return new document_icon('e/anchor'); } + + /** + * Returns a list of category names associated with the area. + * + * @return array + */ + public function get_category_names() { + return [manager::SEARCH_AREA_CATEGORY_COURSE_CONTENT]; + } } diff --git a/search/classes/base_mod.php b/search/classes/base_mod.php index fcf5af636a1..ae5e4590a0a 100644 --- a/search/classes/base_mod.php +++ b/search/classes/base_mod.php @@ -296,4 +296,13 @@ abstract class base_mod extends base { public function get_doc_icon(document $doc) : document_icon { return new document_icon('icon', $this->get_module_name()); } + + /** + * Returns a list of category names associated with the area. + * + * @return array + */ + public function get_category_names() { + return [manager::SEARCH_AREA_CATEGORY_COURSE_CONTENT]; + } } diff --git a/search/classes/manager.php b/search/classes/manager.php index 007c6695b9b..01d3b65c77a 100644 --- a/search/classes/manager.php +++ b/search/classes/manager.php @@ -97,6 +97,31 @@ class manager { */ const INDEX_PRIORITY_REINDEXING = 50; + /** + * @var string Core search area category for all results. + */ + const SEARCH_AREA_CATEGORY_ALL = 'core-all'; + + /** + * @var string Core search area category for course content. + */ + const SEARCH_AREA_CATEGORY_COURSE_CONTENT = 'core-course-content'; + + /** + * @var string Core search area category for courses. + */ + const SEARCH_AREA_CATEGORY_COURSES = 'core-courses'; + + /** + * @var string Core search area category for users. + */ + const SEARCH_AREA_CATEGORY_USERS = 'core-users'; + + /** + * @var string Core search area category for results that do not fit into any of existing categories. + */ + const SEARCH_AREA_CATEGORY_OTHER = 'core-other'; + /** * @var \core_search\base[] Enabled search areas. */ @@ -107,6 +132,11 @@ class manager { */ protected static $allsearchareas = null; + /** + * @var \core_search\area_category[] A list of search area categories. + */ + protected static $searchareacategories = null; + /** * @var \core_search\manager */ @@ -359,6 +389,162 @@ class manager { return static::$allsearchareas; } + /** + * Return search area category instance by category name. + * + * @param string $name Category name. If name is not valid will return default category. + * + * @return \core_search\area_category + */ + public static function get_search_area_category_by_name($name) { + if (key_exists($name, self::get_search_area_categories())) { + return self::get_search_area_categories()[$name]; + } else { + return self::get_search_area_categories()[self::get_default_area_category_name()]; + } + } + + /** + * Return a list of existing search area categories. + * + * @return \core_search\area_category[] + */ + public static function get_search_area_categories() { + if (!isset(static::$searchareacategories)) { + $categories = self::get_core_search_area_categories(); + + // Go through all existing search areas and get categories they are assigned to. + $areacategories = []; + foreach (self::get_search_areas_list() as $searcharea) { + foreach ($searcharea->get_category_names() as $categoryname) { + if (!key_exists($categoryname, $areacategories)) { + $areacategories[$categoryname] = []; + } + + $areacategories[$categoryname][] = $searcharea; + } + } + + // Populate core categories by areas. + foreach ($areacategories as $name => $searchareas) { + if (key_exists($name, $categories)) { + $categories[$name]->set_areas($searchareas); + } else { + throw new \coding_exception('Unknown core search area category ' . $name); + } + } + + // Get additional categories. + $additionalcategories = self::get_additional_search_area_categories(); + foreach ($additionalcategories as $additionalcategory) { + if (!key_exists($additionalcategory->get_name(), $categories)) { + $categories[$additionalcategory->get_name()] = $additionalcategory; + } + } + + // Remove categories without areas. + foreach ($categories as $key => $category) { + if (empty($category->get_areas())) { + unset($categories[$key]); + } + } + + // Sort categories by order. + uasort($categories, function($category1, $category2) { + return $category1->get_order() > $category2->get_order(); + }); + + static::$searchareacategories = $categories; + } + + return static::$searchareacategories; + } + + /** + * Get list of core search area categories. + * + * @return \core_search\area_category[] + */ + protected static function get_core_search_area_categories() { + $categories = []; + + $categories[self::SEARCH_AREA_CATEGORY_ALL] = new area_category( + self::SEARCH_AREA_CATEGORY_ALL, + get_string('core-all', 'search'), + 0, + self::get_search_areas_list(true) + ); + + $categories[self::SEARCH_AREA_CATEGORY_COURSE_CONTENT] = new area_category( + self::SEARCH_AREA_CATEGORY_COURSE_CONTENT, + get_string('core-course-content', 'search'), + 1 + ); + + $categories[self::SEARCH_AREA_CATEGORY_COURSES] = new area_category( + self::SEARCH_AREA_CATEGORY_COURSES, + get_string('core-courses', 'search'), + 2 + ); + + $categories[self::SEARCH_AREA_CATEGORY_USERS] = new area_category( + self::SEARCH_AREA_CATEGORY_USERS, + get_string('core-users', 'search'), + 3 + ); + + $categories[self::SEARCH_AREA_CATEGORY_OTHER] = new area_category( + self::SEARCH_AREA_CATEGORY_OTHER, + get_string('core-other', 'search'), + 4 + ); + + return $categories; + } + + /** + * Gets a list of additional search area categories. + * + * @return \core_search\area_category[] + */ + protected static function get_additional_search_area_categories() { + $additionalcategories = []; + + // Allow plugins to add custom search area categories. + if ($pluginsfunction = get_plugins_with_function('search_area_categories')) { + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $plugincategories = $pluginfunction(); + // We're expecting a list of valid area categories. + if (is_array($plugincategories)) { + foreach ($plugincategories as $plugincategory) { + if (self::is_valid_area_category($plugincategory)) { + $additionalcategories[] = $plugincategory; + } else { + throw new \coding_exception('Invalid search area category!'); + } + } + } else { + throw new \coding_exception($pluginfunction . ' should return a list of search area categories!'); + } + } + } + } + + return $additionalcategories; + } + + /** + * Check if provided instance of area category is valid. + * + * @param mixed $areacategory Area category instance. Potentially could be anything. + * + * @return bool + */ + protected static function is_valid_area_category($areacategory) { + return $areacategory instanceof area_category; + } + /** * Clears all static caches. * @@ -369,6 +555,7 @@ class manager { static::$enabledsearchareas = null; static::$allsearchareas = null; static::$instance = null; + static::$searchareacategories = null; base_block::clear_static(); engine::clear_users_cache(); @@ -671,6 +858,19 @@ class manager { public function paged_search(\stdClass $formdata, $pagenum) { $out = new \stdClass(); + if (self::is_search_area_categories_enabled() && !empty($formdata->cat)) { + $cat = self::get_search_area_category_by_name($formdata->cat); + if (empty($formdata->areaids)) { + $formdata->areaids = array_keys($cat->get_areas()); + } else { + foreach ($formdata->areaids as $key => $areaid) { + if (!key_exists($areaid, $cat->get_areas())) { + unset($formdata->areaids[$key]); + } + } + } + } + $perpage = static::DISPLAY_RESULTS_PER_PAGE; // Make sure we only allow request up to max page. @@ -1435,4 +1635,41 @@ class manager { } return microtime(true); } + + /** + * Check if search area categories functionality is enabled. + * + * @return bool + */ + public static function is_search_area_categories_enabled() { + return !empty(get_config('core', 'searchenablecategories')); + } + + /** + * Check if all results category should be hidden. + * + * @return bool + */ + public static function should_hide_all_results_category() { + return get_config('core', 'searchhideallcategory'); + } + + /** + * Returns default search area category name. + * + * @return string + */ + public static function get_default_area_category_name() { + $default = get_config('core', 'searchdefaultcategory'); + + if (empty($default)) { + $default = self::SEARCH_AREA_CATEGORY_ALL; + } + + if ($default == self::SEARCH_AREA_CATEGORY_ALL && self::should_hide_all_results_category()) { + $default = self::SEARCH_AREA_CATEGORY_COURSE_CONTENT; + } + + return $default; + } } diff --git a/search/classes/output/form/search.php b/search/classes/output/form/search.php index 8d633df60be..f4c8e459c44 100644 --- a/search/classes/output/form/search.php +++ b/search/classes/output/form/search.php @@ -40,6 +40,13 @@ class search extends \moodleform { global $USER, $DB, $OUTPUT; $mform =& $this->_form; + + if (\core_search\manager::is_search_area_categories_enabled() && !empty($this->_customdata['cat'])) { + $mform->addElement('hidden', 'cat'); + $mform->setType('cat', PARAM_NOTAGS); + $mform->setDefault('cat', $this->_customdata['cat']); + } + $mform->disable_form_change_checker(); $mform->addElement('header', 'search', get_string('search', 'search')); @@ -72,11 +79,21 @@ class search extends \moodleform { $mform->setType('title', PARAM_TEXT); $search = \core_search\manager::instance(true); - - $searchareas = \core_search\manager::get_search_areas_list(true); + $enabledsearchareas = \core_search\manager::get_search_areas_list(true); $areanames = array(); - foreach ($searchareas as $areaid => $searcharea) { - $areanames[$areaid] = $searcharea->get_visible_name(); + + if (\core_search\manager::is_search_area_categories_enabled() && !empty($this->_customdata['cat'])) { + $searchareacategory = \core_search\manager::get_search_area_category_by_name($this->_customdata['cat']); + $searchareas = $searchareacategory->get_areas(); + foreach ($searchareas as $areaid => $searcharea) { + if (key_exists($areaid, $enabledsearchareas)) { + $areanames[$areaid] = $searcharea->get_visible_name(); + } + } + } else { + foreach ($enabledsearchareas as $areaid => $searcharea) { + $areanames[$areaid] = $searcharea->get_visible_name(); + } } // Sort the array by the text. diff --git a/search/classes/output/renderer.php b/search/classes/output/renderer.php index 97cbe29e110..793611d8a10 100644 --- a/search/classes/output/renderer.php +++ b/search/classes/output/renderer.php @@ -53,13 +53,32 @@ class renderer extends \plugin_renderer_base { * @param int $page Zero based page number. * @param int $totalcount Total number of results available. * @param \moodle_url $url + * @param \core_search\area_category|null $cat Selected search are category or null if category functionality is disabled. * @return string HTML */ - public function render_results($results, $page, $totalcount, $url) { + public function render_results($results, $page, $totalcount, $url, $cat = null) { + $content = ''; + + if (\core_search\manager::is_search_area_categories_enabled() && !empty($cat)) { + $toprow = []; + foreach (\core_search\manager::get_search_area_categories() as $category) { + $taburl = clone $url; + $taburl->param('cat', $category->get_name()); + $taburl->param('page', 0); + $taburl->remove_params(['page', 'areaids']); + $toprow[$category->get_name()] = new \tabobject($category->get_name(), $taburl, $category->get_visiblename()); + } + + if (\core_search\manager::should_hide_all_results_category()) { + unset($toprow[\core_search\manager::SEARCH_AREA_CATEGORY_ALL]); + } + + $content .= $this->tabtree($toprow, $cat->get_name()); + } // Paging bar. $perpage = \core_search\manager::DISPLAY_RESULTS_PER_PAGE; - $content = $this->output->paging_bar($totalcount, $page, $perpage, $url); + $content .= $this->output->paging_bar($totalcount, $page, $perpage, $url); // Results. $resultshtml = array(); diff --git a/search/index.php b/search/index.php index 02f7ea83666..ef80dc333bc 100644 --- a/search/index.php +++ b/search/index.php @@ -28,6 +28,12 @@ $page = optional_param('page', 0, PARAM_INT); $q = optional_param('q', '', PARAM_NOTAGS); $title = optional_param('title', '', PARAM_NOTAGS); $contextid = optional_param('context', 0, PARAM_INT); +$cat = optional_param('cat', '', PARAM_NOTAGS); + +if (\core_search\manager::is_search_area_categories_enabled()) { + $cat = \core_search\manager::get_search_area_category_by_name($cat); +} + // Moving areaids, courseids, timestart, and timeend further down as they might come as an array if they come from the form. $context = context_system::instance(); @@ -80,6 +86,10 @@ if ($contextid) { // Get available ordering options from search engine. $customdata['orderoptions'] = $search->get_engine()->get_supported_orders($context); +if ($cat instanceof \core_search\area_category) { + $customdata['cat'] = $cat->get_name(); +} + $mform = new \core_search\output\form\search(null, $customdata); $data = $mform->get_data(); @@ -125,6 +135,10 @@ if (!empty($context) && $data) { $data->context = $context; } +if ($data && $cat instanceof \core_search\area_category) { + $data->cat = $cat->get_name(); +} + // Set the page URL. $urlparams = array('page' => $page); if ($data) { @@ -139,6 +153,11 @@ if ($data) { $urlparams['timestart'] = $data->timestart; $urlparams['timeend'] = $data->timeend; } + +if ($cat instanceof \core_search\area_category) { + $urlparams['cat'] = $cat->get_name(); +} + $url = new moodle_url('/search/index.php', $urlparams); $PAGE->set_url($url); @@ -160,7 +179,7 @@ if ($errorstr = $search->get_engine()->get_query_error()) { $mform->display(); if (!empty($results)) { - echo $searchrenderer->render_results($results->results, $results->actualpage, $results->totalcount, $url); + echo $searchrenderer->render_results($results->results, $results->actualpage, $results->totalcount, $url, $cat); \core_search\manager::trigger_search_results_viewed([ 'q' => $data->q, diff --git a/search/tests/area_category_test.php b/search/tests/area_category_test.php new file mode 100644 index 00000000000..9aa25625aa1 --- /dev/null +++ b/search/tests/area_category_test.php @@ -0,0 +1,120 @@ +. + +/** + * Area category unit tests. + * + * @package core_search + * @copyright 2018 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Area category unit tests. + * + * @package core_search + * @copyright 2018 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class search_area_category_testcase extends advanced_testcase { + + /** + * A helper function to get a mocked search area. + * @param string $areaid + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function get_mocked_area($areaid) { + $builder = $this->getMockBuilder('\core_search\base'); + $builder->disableOriginalConstructor(); + $builder->setMethods(array('get_area_id')); + $area = $builder->getMockForAbstractClass(); + $area->method('get_area_id')->willReturn($areaid); + + return $area; + } + + /** + * A helper function to get a list of search areas. + * + * @return array + */ + protected function get_areas() { + $areas = []; + $areas[] = $this->get_mocked_area('area1'); + $areas[] = 'String'; + $areas[] = 1; + $areas[] = '12'; + $areas[] = true; + $areas[] = false; + $areas[] = null; + $areas[] = [$this->get_mocked_area('area2')]; + $areas[] = $this; + $areas[] = new stdClass(); + $areas[] = $this->get_mocked_area('area3'); + $areas[] = $this->get_mocked_area('area4'); + + return $areas; + } + + /** + * Test default values. + */ + public function test_default_values() { + $category = new \core_search\area_category('test_name', 'test_visiblename'); + + $this->assertEquals('test_name', $category->get_name()); + $this->assertEquals('test_visiblename', $category->get_visiblename()); + $this->assertEquals(0, $category->get_order()); + $this->assertEquals([], $category->get_areas()); + } + + /** + * Test that all get functions work as expected. + */ + public function test_getters() { + $category = new \core_search\area_category('test_name', 'test_visiblename', 4, $this->get_areas()); + + $this->assertEquals('test_name', $category->get_name()); + $this->assertEquals('test_visiblename', $category->get_visiblename()); + $this->assertEquals(4, $category->get_order()); + + $this->assertTrue(is_array($category->get_areas())); + $this->assertCount(3, $category->get_areas()); + $this->assertTrue(key_exists('area1', $category->get_areas())); + $this->assertTrue(key_exists('area3', $category->get_areas())); + $this->assertTrue(key_exists('area4', $category->get_areas())); + } + + /** + * Test that a list of areas could be set correctly. + */ + public function test_list_of_areas_could_be_set() { + $category = new \core_search\area_category('test_name', 'test_visiblename'); + $this->assertEquals([], $category->get_areas()); + + $category->set_areas($this->get_areas()); + + $this->assertTrue(is_array($category->get_areas())); + $this->assertCount(3, $category->get_areas()); + $this->assertTrue(key_exists('area1', $category->get_areas())); + $this->assertTrue(key_exists('area3', $category->get_areas())); + $this->assertTrue(key_exists('area4', $category->get_areas())); + } + +} diff --git a/search/tests/base_test.php b/search/tests/base_test.php index 0d7cb18c1b5..dcce079410e 100644 --- a/search/tests/base_test.php +++ b/search/tests/base_test.php @@ -157,4 +157,16 @@ class search_base_testcase extends advanced_testcase { $this->assertEquals('i/empty', $result->get_name()); $this->assertEquals('moodle', $result->get_component()); } + + /** + * Test base search area category names. + */ + public function test_get_category_names() { + $builder = $this->getMockBuilder('\core_search\base'); + $builder->disableOriginalConstructor(); + $stub = $builder->getMockForAbstractClass(); + + $expected = ['core-course-content']; + $this->assertEquals($expected, $stub->get_category_names()); + } } diff --git a/search/tests/manager_test.php b/search/tests/manager_test.php index 124e04ab984..d978eff42d0 100644 --- a/search/tests/manager_test.php +++ b/search/tests/manager_test.php @@ -1210,4 +1210,83 @@ class search_manager_testcase extends advanced_testcase { // Confirm request table is now empty. $this->assertEquals(0, $DB->count_records('search_index_requests')); } + + /** + * Test search area categories. + */ + public function test_get_search_area_categories() { + $categories = \core_search\manager::get_search_area_categories(); + + $this->assertTrue(is_array($categories)); + $this->assertTrue(count($categories) >= 4); // We always should have 4 core categories. + $this->assertArrayHasKey('core-all', $categories); + $this->assertArrayHasKey('core-course-content', $categories); + $this->assertArrayHasKey('core-courses', $categories); + $this->assertArrayHasKey('core-users', $categories); + + foreach ($categories as $category) { + $this->assertInstanceOf('\core_search\area_category', $category); + } + } + + /** + * Test that we can find out if search area categories functionality is enabled. + */ + public function test_is_search_area_categories_enabled() { + $this->resetAfterTest(); + + $this->assertFalse(\core_search\manager::is_search_area_categories_enabled()); + set_config('searchenablecategories', 1); + $this->assertTrue(\core_search\manager::is_search_area_categories_enabled()); + set_config('searchenablecategories', 0); + $this->assertFalse(\core_search\manager::is_search_area_categories_enabled()); + } + + /** + * Test that we can find out if hiding all results category is enabled. + */ + public function test_should_hide_all_results_category() { + $this->resetAfterTest(); + + $this->assertEquals(0, \core_search\manager::should_hide_all_results_category()); + set_config('searchhideallcategory', 1); + $this->assertEquals(1, \core_search\manager::should_hide_all_results_category()); + set_config('searchhideallcategory', 0); + $this->assertEquals(0, \core_search\manager::should_hide_all_results_category()); + } + + /** + * Test that we can get default search category name. + */ + public function test_get_default_area_category_name() { + $this->resetAfterTest(); + + $expected = 'core-all'; + $this->assertEquals($expected, \core_search\manager::get_default_area_category_name()); + + set_config('searchhideallcategory', 1); + $expected = 'core-course-content'; + $this->assertEquals($expected, \core_search\manager::get_default_area_category_name()); + + set_config('searchhideallcategory', 0); + $expected = 'core-all'; + $this->assertEquals($expected, \core_search\manager::get_default_area_category_name()); + } + + /** + * Test that we can get correct search area category by its name. + */ + public function test_get_search_area_category_by_name() { + $this->resetAfterTest(); + + $testcategory = \core_search\manager::get_search_area_category_by_name('test_random_name'); + $this->assertEquals('core-all', $testcategory->get_name()); + + $testcategory = \core_search\manager::get_search_area_category_by_name('core-courses'); + $this->assertEquals('core-courses', $testcategory->get_name()); + + set_config('searchhideallcategory', 1); + $testcategory = \core_search\manager::get_search_area_category_by_name('test_random_name'); + $this->assertEquals('core-course-content', $testcategory->get_name()); + } } diff --git a/user/classes/search/user.php b/user/classes/search/user.php index 479b4e08230..03f034a71a6 100644 --- a/user/classes/search/user.php +++ b/user/classes/search/user.php @@ -216,4 +216,13 @@ class user extends \core_search\base { return new \core_search\document_icon('i/user'); } + /** + * Returns a list of category names associated with the area. + * + * @return array + */ + public function get_category_names() { + return [\core_search\manager::SEARCH_AREA_CATEGORY_USERS]; + } + } diff --git a/user/tests/search_test.php b/user/tests/search_test.php index 030145ffc16..710681d5fd8 100644 --- a/user/tests/search_test.php +++ b/user/tests/search_test.php @@ -229,4 +229,14 @@ class user_search_testcase extends advanced_testcase { $this->assertEquals('i/user', $result->get_name()); $this->assertEquals('moodle', $result->get_component()); } + + /** + * Test assigned search categories. + */ + public function test_get_category_names() { + $searcharea = \core_search\manager::get_search_area($this->userareaid); + + $expected = ['core-users']; + $this->assertEquals($expected, $searcharea->get_category_names()); + } }