From 4e921569d03186b51d93f6fafceb7e72068b7020 Mon Sep 17 00:00:00 2001 From: Matt Porritt Date: Thu, 6 Jul 2017 15:27:53 +1000 Subject: [PATCH] MDL-59459 Golbal Search: Increase file indexing coverage Increase the scope of the files that are detected and indexed by Moodle's Global Search. This includes intro and content areas of search areas. The use case here is in a description for an activity there is a file added that contains more information about the activity. It would an advantage to be able to search this content as well. It would also allow search engine plugins to be able to index non text files such as images. --- course/classes/search/mycourse.php | 37 +++++ mod/assign/classes/search/activity.php | 21 +-- mod/book/classes/search/activity.php | 9 ++ mod/book/classes/search/chapter.php | 21 +++ mod/chat/classes/search/activity.php | 9 ++ mod/choice/classes/search/activity.php | 9 ++ mod/data/classes/search/activity.php | 10 ++ mod/feedback/classes/search/activity.php | 9 ++ mod/folder/classes/search/activity.php | 20 +-- mod/forum/classes/search/activity.php | 9 ++ mod/forum/classes/search/post.php | 32 +++- mod/glossary/classes/search/activity.php | 9 ++ mod/glossary/classes/search/entry.php | 21 +++ mod/imscp/classes/search/activity.php | 9 ++ mod/label/classes/search/activity.php | 9 ++ mod/lesson/classes/search/activity.php | 21 +++ mod/lti/classes/search/activity.php | 9 ++ mod/page/classes/search/activity.php | 22 ++- mod/quiz/classes/search/activity.php | 9 ++ mod/resource/classes/search/activity.php | 23 +-- mod/resource/tests/search_test.php | 15 +- mod/scorm/classes/search/activity.php | 9 ++ mod/survey/classes/search/activity.php | 9 ++ mod/url/classes/search/activity.php | 9 ++ mod/wiki/classes/search/activity.php | 9 ++ .../classes/search/collaborative_page.php | 21 +++ mod/workshop/classes/search/activity.php | 21 +++ search/classes/base.php | 39 ++++- search/classes/base_activity.php | 61 ++++++-- search/classes/base_mod.php | 12 +- search/tests/base_activity_test.php | 141 ++++++++++++++++++ search/tests/base_test.php | 132 ++++++++++++++++ user/classes/search/user.php | 37 +++++ 33 files changed, 765 insertions(+), 68 deletions(-) create mode 100644 search/tests/base_activity_test.php create mode 100644 search/tests/base_test.php diff --git a/course/classes/search/mycourse.php b/course/classes/search/mycourse.php index 833f7fbc777..8972162f603 100644 --- a/course/classes/search/mycourse.php +++ b/course/classes/search/mycourse.php @@ -124,4 +124,41 @@ class mycourse extends \core_search\base { public function get_context_url(\core_search\document $doc) { return new \moodle_url('/course/view.php', array('id' => $doc->get('courseid'))); } + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + + /** + * Return the context info required to index files for + * this search area. + * + * Should be overridden by each search area. + * + * @return array + */ + public function get_search_fileareas() { + $fileareas = array( + 'overviewfiles', + 'summary'// Fileareas. + ); + + return $fileareas; + } + + /** + * Returns the moodle component name. + * + * It might be the plugin name (whole frankenstyle name) or the core subsystem name. + * + * @return string + */ + public function get_component_name() { + return 'course'; + } } diff --git a/mod/assign/classes/search/activity.php b/mod/assign/classes/search/activity.php index ea5a69c0278..7517d9ca06c 100644 --- a/mod/assign/classes/search/activity.php +++ b/mod/assign/classes/search/activity.php @@ -36,6 +36,7 @@ require_once($CFG->dirroot . '/mod/assign/locallib.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + /** * Returns true if this area uses file indexing. * @@ -46,22 +47,14 @@ class activity extends \core_search\base_activity { } /** - * Add the attached description files. + * Return the context info required to index files for + * this search area. * - * @param document $document The current document - * @return null + * @return array */ - public function attach_files($document) { - $fs = get_file_storage(); + public function get_search_fileareas() { + $fileareas = array('intro', ASSIGN_INTROATTACHMENT_FILEAREA); // Fileareas. - $cm = $this->get_cm($this->get_module_name(), $document->get('itemid'), $document->get('courseid')); - $context = \context_module::instance($cm->id); - - $files = $fs->get_area_files($context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0, - 'sortorder DESC, id ASC', false); - - foreach ($files as $file) { - $document->add_stored_file($file); - } + return $fileareas; } } diff --git a/mod/book/classes/search/activity.php b/mod/book/classes/search/activity.php index 862e42ec66f..dac6b7b5517 100644 --- a/mod/book/classes/search/activity.php +++ b/mod/book/classes/search/activity.php @@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } } diff --git a/mod/book/classes/search/chapter.php b/mod/book/classes/search/chapter.php index 021b58cb607..3acd1866539 100644 --- a/mod/book/classes/search/chapter.php +++ b/mod/book/classes/search/chapter.php @@ -158,4 +158,25 @@ class chapter extends \core_search\base_mod { $contextmodule = \context::instance_by_id($doc->get('contextid')); return new \moodle_url('/mod/book/view.php', array('id' => $contextmodule->instanceid)); } + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + + /** + * Return the context info required to index files for + * this search area. + * + * @return array + */ + public function get_search_fileareas() { + $fileareas = array('chapter'); // Filearea. + + return $fileareas; + } } diff --git a/mod/chat/classes/search/activity.php b/mod/chat/classes/search/activity.php index 067dd6e5034..98bed370926 100644 --- a/mod/chat/classes/search/activity.php +++ b/mod/chat/classes/search/activity.php @@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } } diff --git a/mod/choice/classes/search/activity.php b/mod/choice/classes/search/activity.php index af2bd919eef..0c7eb2fd88f 100644 --- a/mod/choice/classes/search/activity.php +++ b/mod/choice/classes/search/activity.php @@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } } diff --git a/mod/data/classes/search/activity.php b/mod/data/classes/search/activity.php index 7f1f6098bfc..75fc28046b3 100644 --- a/mod/data/classes/search/activity.php +++ b/mod/data/classes/search/activity.php @@ -34,4 +34,14 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + } diff --git a/mod/feedback/classes/search/activity.php b/mod/feedback/classes/search/activity.php index 84755b43801..eee1f8338bd 100644 --- a/mod/feedback/classes/search/activity.php +++ b/mod/feedback/classes/search/activity.php @@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } } diff --git a/mod/folder/classes/search/activity.php b/mod/folder/classes/search/activity.php index 08093372a48..51175e2cc83 100644 --- a/mod/folder/classes/search/activity.php +++ b/mod/folder/classes/search/activity.php @@ -34,6 +34,7 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + /** * Returns true if this area uses file indexing. * @@ -44,21 +45,14 @@ class activity extends \core_search\base_activity { } /** - * Add all the folder files to the index. + * Return the context info required to index files for + * this search area. * - * @param document $document The current document - * @return null + * @return array */ - public function attach_files($document) { - $fs = get_file_storage(); + public function get_search_fileareas() { + $fileareas = array('intro', 'content'); // Fileareas. - $cm = $this->get_cm($this->get_module_name(), $document->get('itemid'), $document->get('courseid')); - $context = \context_module::instance($cm->id); - - $files = $fs->get_area_files($context->id, 'mod_folder', 'content', 0, 'sortorder DESC, id ASC', false); - - foreach ($files as $file) { - $document->add_stored_file($file); - } + return $fileareas; } } diff --git a/mod/forum/classes/search/activity.php b/mod/forum/classes/search/activity.php index 6435104897d..82397586a4e 100644 --- a/mod/forum/classes/search/activity.php +++ b/mod/forum/classes/search/activity.php @@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } } diff --git a/mod/forum/classes/search/post.php b/mod/forum/classes/search/post.php index 8c43e4ef3e7..bb48a035304 100644 --- a/mod/forum/classes/search/post.php +++ b/mod/forum/classes/search/post.php @@ -120,6 +120,21 @@ class post extends \core_search\base_mod { return true; } + /** + * Return the context info required to index files for + * this search area. + * + * @return array + */ + public function get_search_fileareas() { + $fileareas = array( + 'attachment', + 'post' + ); + + return $fileareas; + } + /** * Add the forum post attachments. * @@ -142,14 +157,21 @@ class post extends \core_search\base_mod { // Because this is used during indexing, we don't want to cache posts. Would result in memory leak. unset($this->postsdata[$postid]); - $cm = $this->get_cm('forum', $post->forum, $document->get('courseid')); + $cm = $this->get_cm($this->get_module_name(), $post->forum, $document->get('courseid')); $context = \context_module::instance($cm->id); + $contextid = $context->id; + + $fileareas = $this->get_search_fileareas(); + $component = $this->get_component_name(); // Get the files and attach them. - $fs = get_file_storage(); - $files = $fs->get_area_files($context->id, 'mod_forum', 'attachment', $postid, "filename", false); - foreach ($files as $file) { - $document->add_stored_file($file); + foreach ($fileareas as $filearea) { + $fs = get_file_storage(); + $files = $fs->get_area_files($contextid, $component, $filearea, $postid, '', false); + + foreach ($files as $file) { + $document->add_stored_file($file); + } } } diff --git a/mod/glossary/classes/search/activity.php b/mod/glossary/classes/search/activity.php index 82b34e2363f..d7daef8c873 100644 --- a/mod/glossary/classes/search/activity.php +++ b/mod/glossary/classes/search/activity.php @@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } } diff --git a/mod/glossary/classes/search/entry.php b/mod/glossary/classes/search/entry.php index 930ec3230ac..791e3414ddc 100644 --- a/mod/glossary/classes/search/entry.php +++ b/mod/glossary/classes/search/entry.php @@ -193,4 +193,25 @@ class entry extends \core_search\base_mod { } return $this->entriesdata[$entryid]; } + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + + /** + * Return the context info required to index files for + * this search area. + * + * @return array + */ + public function get_search_fileareas() { + $fileareas = array('attachment', 'entry'); // Fileareas. + + return $fileareas; + } } diff --git a/mod/imscp/classes/search/activity.php b/mod/imscp/classes/search/activity.php index 04aa1b8d4f0..9a1a052a53c 100644 --- a/mod/imscp/classes/search/activity.php +++ b/mod/imscp/classes/search/activity.php @@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } } diff --git a/mod/label/classes/search/activity.php b/mod/label/classes/search/activity.php index 2ddd70684cd..a8f7159dc00 100644 --- a/mod/label/classes/search/activity.php +++ b/mod/label/classes/search/activity.php @@ -38,6 +38,15 @@ defined('MOODLE_INTERNAL') || die(); */ class activity extends \core_search\base_activity { + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + /** * Overwritten as labels are displayed in-course. * diff --git a/mod/lesson/classes/search/activity.php b/mod/lesson/classes/search/activity.php index 1f93104d2c0..bb24fcf80c9 100644 --- a/mod/lesson/classes/search/activity.php +++ b/mod/lesson/classes/search/activity.php @@ -34,4 +34,25 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + + /** + * Return the context info required to index files for + * this search area. + * + * @return array + */ + public function get_search_fileareas() { + $fileareas = array('intro', 'page_contents'); // Fileareas. + + return $fileareas; + } } diff --git a/mod/lti/classes/search/activity.php b/mod/lti/classes/search/activity.php index 47d8177d293..269ad7ae708 100644 --- a/mod/lti/classes/search/activity.php +++ b/mod/lti/classes/search/activity.php @@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } } diff --git a/mod/page/classes/search/activity.php b/mod/page/classes/search/activity.php index c66c66fc58f..6a7b41ca6a5 100644 --- a/mod/page/classes/search/activity.php +++ b/mod/page/classes/search/activity.php @@ -35,7 +35,6 @@ defined('MOODLE_INTERNAL') || die(); */ class activity extends \core_search\base_activity { - /** * Returns the document associated with this activity. * @@ -74,4 +73,25 @@ class activity extends \core_search\base_activity { return $doc; } + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + + /** + * Return the context info required to index files for + * this search area. + * + * @return array + */ + public function get_search_fileareas() { + $fileareas = array('intro', 'content'); // Fileareas. + + return $fileareas; + } } diff --git a/mod/quiz/classes/search/activity.php b/mod/quiz/classes/search/activity.php index a4bb3f6a948..bdb90c282d0 100644 --- a/mod/quiz/classes/search/activity.php +++ b/mod/quiz/classes/search/activity.php @@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } } diff --git a/mod/resource/classes/search/activity.php b/mod/resource/classes/search/activity.php index 7490ab578ed..2af10ad3fb5 100644 --- a/mod/resource/classes/search/activity.php +++ b/mod/resource/classes/search/activity.php @@ -34,6 +34,7 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + /** * Returns true if this area uses file indexing. * @@ -44,24 +45,14 @@ class activity extends \core_search\base_activity { } /** - * Add the main file to the index. + * Return the context info required to index files for + * this search area. * - * @param document $document The current document - * @return null + * @return array */ - public function attach_files($document) { - $fs = get_file_storage(); + public function get_search_fileareas() { + $fileareas = array('intro', 'content'); // Fileareas. - $cm = $this->get_cm($this->get_module_name(), $document->get('itemid'), $document->get('courseid')); - $context = \context_module::instance($cm->id); - - // Order by sortorder desc, the first is consided the main file. - $files = $fs->get_area_files($context->id, 'mod_resource', 'content', 0, 'sortorder DESC, id ASC', false); - - $mainfile = $files ? reset($files) : null; - if ($mainfile && $mainfile->get_sortorder() > 0) { - $document->add_stored_file($mainfile); - } + return $fileareas; } - } diff --git a/mod/resource/tests/search_test.php b/mod/resource/tests/search_test.php index b4b7d09ae80..c5d04dd3884 100644 --- a/mod/resource/tests/search_test.php +++ b/mod/resource/tests/search_test.php @@ -84,7 +84,7 @@ class mod_resource_search_testcase extends advanced_testcase { ); $fs->create_file_from_string($filerecord, 'Test resource file'); - // Attach a second file that shouldn't be returned with the search doc. + // Attach a second file. $filerecord['filename'] = 'extrafile'; $filerecord['sortorder'] = 0; $fs->create_file_from_string($filerecord, 'Test resource file 2'); @@ -101,10 +101,15 @@ class mod_resource_search_testcase extends advanced_testcase { $searcharea->attach_files($doc); $files = $doc->get_files(); - // Resources should only return their main file. - $this->assertCount(1, $files); - $file = reset($files); - $this->assertEquals('mainfile', $file->get_filename()); + // Resources should return all added files. + $this->assertCount(2, $files); + + $filenames = array(); + foreach ($files as $file) { + $filenames[] = $file->get_filename(); + } + $this->assertContains('mainfile', $filenames); + $this->assertContains('extrafile', $filenames); $nrecords++; } diff --git a/mod/scorm/classes/search/activity.php b/mod/scorm/classes/search/activity.php index 75ad2a1ed75..906066bf256 100644 --- a/mod/scorm/classes/search/activity.php +++ b/mod/scorm/classes/search/activity.php @@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } } diff --git a/mod/survey/classes/search/activity.php b/mod/survey/classes/search/activity.php index 63751825ca9..fd7914175fd 100644 --- a/mod/survey/classes/search/activity.php +++ b/mod/survey/classes/search/activity.php @@ -35,6 +35,15 @@ defined('MOODLE_INTERNAL') || die(); */ class activity extends \core_search\base_activity { + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + /** * Returns recordset containing required data for indexing activities. * diff --git a/mod/url/classes/search/activity.php b/mod/url/classes/search/activity.php index 97f3515719b..c5baae213b5 100644 --- a/mod/url/classes/search/activity.php +++ b/mod/url/classes/search/activity.php @@ -35,6 +35,15 @@ defined('MOODLE_INTERNAL') || die(); */ class activity extends \core_search\base_activity { + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + /** * Returns the document associated with this activity. * diff --git a/mod/wiki/classes/search/activity.php b/mod/wiki/classes/search/activity.php index e3812b518d6..d1c6feb03e6 100644 --- a/mod/wiki/classes/search/activity.php +++ b/mod/wiki/classes/search/activity.php @@ -34,4 +34,13 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } } diff --git a/mod/wiki/classes/search/collaborative_page.php b/mod/wiki/classes/search/collaborative_page.php index 987b1b75272..be3a16a62c0 100644 --- a/mod/wiki/classes/search/collaborative_page.php +++ b/mod/wiki/classes/search/collaborative_page.php @@ -175,4 +175,25 @@ class collaborative_page extends \core_search\base_mod { $contextmodule = \context::instance_by_id($doc->get('contextid')); return new \moodle_url('/mod/wiki/view.php', array('id' => $contextmodule->instanceid)); } + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + + /** + * Return the context info required to index files for + * this search area. + * + * @return array + */ + public function get_search_fileareas() { + $fileareas = array('attachments'); // Filearea. + + return $fileareas; + } } diff --git a/mod/workshop/classes/search/activity.php b/mod/workshop/classes/search/activity.php index 28445887be4..5ebec2d0f39 100644 --- a/mod/workshop/classes/search/activity.php +++ b/mod/workshop/classes/search/activity.php @@ -34,4 +34,25 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class activity extends \core_search\base_activity { + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + + /** + * Return the context info required to index files for + * this search area. + * + * @return array + */ + public function get_search_fileareas() { + $fileareas = array('intro', 'instructauthors', 'instructreviewers', 'conclusion'); // Fileareas. + + return $fileareas; + } } diff --git a/search/classes/base.php b/search/classes/base.php index 6fef4650eea..915555df95f 100644 --- a/search/classes/base.php +++ b/search/classes/base.php @@ -274,13 +274,48 @@ abstract class base { abstract public function get_document($record, $options = array()); /** - * Add any files to the document that should be indexed. + * Return the context info required to index files for + * this search area. + * + * Should be onerridden by each search area. + * + * @return array + */ + public function get_search_fileareas() { + $fileareas = array(); + + return $fileareas; + } + + /** + * Files related to the current document are attached, + * to the document object ready for indexing by + * Global Search. + * + * The default implementation retrieves all files for + * the file areas returned by get_search_fileareas(). + * If you need to filter files to specific items per + * file area, you will need to override this method + * and explicitly provide the items. * * @param document $document The current document * @return void */ public function attach_files($document) { - return; + $fileareas = $this->get_search_fileareas(); + $contextid = $document->get('contextid'); + $component = $this->get_component_name(); + $itemid = $document->get('itemid'); + + foreach ($fileareas as $filearea) { + $fs = get_file_storage(); + $files = $fs->get_area_files($contextid, $component, $filearea, $itemid, '', false); + + foreach ($files as $file) { + $document->add_stored_file($file); + } + } + } /** diff --git a/search/classes/base_activity.php b/search/classes/base_activity.php index b2fda829a01..d6702b98029 100644 --- a/search/classes/base_activity.php +++ b/search/classes/base_activity.php @@ -149,7 +149,7 @@ abstract class base_activity extends base_mod { * @param \core_search\document $doc * @return \moodle_url */ - public function get_doc_url(\core_search\document $doc) {; + public function get_doc_url(\core_search\document $doc) { return $this->get_context_url($doc); } @@ -164,15 +164,6 @@ abstract class base_activity extends base_mod { return new \moodle_url('/mod/' . $this->get_module_name() . '/view.php', array('id' => $cminfo->id)); } - /** - * Returns the module name. - * - * @return string - */ - protected function get_module_name() { - return substr($this->componentname, 4); - } - /** * Returns an activity instance. Internally uses the class component to know which activity module should be retrieved. * @@ -189,4 +180,54 @@ abstract class base_activity extends base_mod { return $this->activitiesdata[$this->get_module_name()][$instanceid]; } + + /** + * Return the context info required to index files for + * this search area. + * + * Should be onerridden by each search area. + * + * @return array + */ + public function get_search_fileareas() { + $fileareas = array( + 'intro' // Fileareas. + ); + + return $fileareas; + } + + /** + * Files related to the current document are attached, + * to the document object ready for indexing by + * Global Search. + * + * The default implementation retrieves all files for + * the file areas returned by get_search_fileareas(). + * If you need to filter files to specific items per + * file area, you will need to override this method + * and explicitly provide the items. + * + * @param document $document The current document + * @return void + */ + public function attach_files($document) { + $fileareas = $this->get_search_fileareas(); + + if (!empty($fileareas)) { + $cm = $this->get_cm($this->get_module_name(), $document->get('itemid'), $document->get('courseid')); + + $context = \context_module::instance($cm->id); + $contextid = $context->id; + + $fs = get_file_storage(); + $files = $fs->get_area_files($contextid, $this->get_component_name(), $fileareas, false, '', false); + + foreach ($files as $file) { + $document->add_stored_file($file); + } + } + + return; + } } diff --git a/search/classes/base_mod.php b/search/classes/base_mod.php index 61eadad519f..bf570478b33 100644 --- a/search/classes/base_mod.php +++ b/search/classes/base_mod.php @@ -48,6 +48,15 @@ abstract class base_mod extends base { */ protected static $levels = [CONTEXT_MODULE]; + /** + * Returns the module name. + * + * @return string + */ + protected function get_module_name() { + return substr($this->componentname, 4); + } + /** * Gets the course module for the required instanceid + modulename. * @@ -75,7 +84,6 @@ abstract class base_mod extends base { // Nothing found. throw new \dml_missing_record_exception($modulename); - - return $cm; } + } diff --git a/search/tests/base_activity_test.php b/search/tests/base_activity_test.php new file mode 100644 index 00000000000..1d255d27d2f --- /dev/null +++ b/search/tests/base_activity_test.php @@ -0,0 +1,141 @@ +. + +/** + * Search engine base unit tests. + * + * @package core_search + * @copyright 2017 Matt Porritt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once(__DIR__ . '/fixtures/testable_core_search.php'); +require_once($CFG->dirroot . '/search/tests/fixtures/mock_search_area.php'); + +/** + * Search engine base unit tests. + * + * @package core_search + * @copyright 2017 Matt Porritt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class search_base_activity_testcase extends advanced_testcase { + /** + * @var \core_search::manager + */ + protected $search = null; + + /** + * @var Instace of core_search_generator. + */ + protected $generator = null; + + /** + * @var Instace of testable_engine. + */ + protected $engine = null; + + public function setUp() { + $this->resetAfterTest(); + set_config('enableglobalsearch', true); + + // Set \core_search::instance to the mock_search_engine as we don't require the search engine to be working to test this. + $search = testable_core_search::instance(); + + $this->generator = self::getDataGenerator()->get_plugin_generator('core_search'); + $this->generator->setup(); + } + + public function tearDown() { + // For unit tests before PHP 7, teardown is called even on skip. So only do our teardown if we did setup. + if ($this->generator) { + // Moodle DML freaks out if we don't teardown the temp table after each run. + $this->generator->teardown(); + $this->generator = null; + } + } + + /** + * Test base activity get search fileareas + */ + public function test_get_search_fileareas_base() { + + $builder = $this->getMockBuilder('\core_search\base_activity'); + $builder->disableOriginalConstructor(); + $stub = $builder->getMockForAbstractClass(); + + $result = $stub->get_search_fileareas(); + + $this->assertEquals(array('intro'), $result); + } + + /** + * Test base attach files + */ + public function test_attach_files_base() { + $filearea = 'intro'; + $component = 'mod_forum'; + $module = 'forum'; + + $course = self::getDataGenerator()->create_course(); + $activity = self::getDataGenerator()->create_module('forum', array('course' => $course->id)); + $context = \context_module::instance($activity->cmid); + $contextid = $context->id; + + // Create file to add. + $fs = get_file_storage(); + $filerecord = array( + 'contextid' => $contextid, + 'component' => $component, + 'filearea' => $filearea, + 'itemid' => 0, + 'filepath' => '/', + 'filename' => 'testfile.txt'); + $content = 'All the news that\'s fit to print'; + $file = $fs->create_file_from_string($filerecord, $content); + + // Construct the search document. + $rec = new \stdClass(); + $rec->courseid = $course->id; + $area = new core_mocksearch\search\mock_search_area(); + $record = $this->generator->create_record($rec); + + $document = $area->get_document($record); + $document->set('itemid', $activity->id); + + // Create a mock from the abstract class, + // with required methods stubbed. + $builder = $this->getMockBuilder('\core_search\base_activity'); + $builder->disableOriginalConstructor(); + $builder->setMethods(array('get_module_name', 'get_component_name')); + $stub = $builder->getMockForAbstractClass(); + $stub->method('get_module_name')->willReturn($module); + $stub->method('get_component_name')->willReturn($component); + + // Attach file to our test document. + $stub->attach_files($document); + + // Verify file is attached. + $files = $document->get_files(); + $file = array_values($files)[0]; + + $this->assertEquals(1, count($files)); + $this->assertEquals($content, $file->get_content()); + } +} diff --git a/search/tests/base_test.php b/search/tests/base_test.php new file mode 100644 index 00000000000..6b5d0f006a5 --- /dev/null +++ b/search/tests/base_test.php @@ -0,0 +1,132 @@ +. + +/** + * Search engine base unit tests. + * + * @package core_search + * @copyright 2017 Matt Porritt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once(__DIR__ . '/fixtures/testable_core_search.php'); +require_once($CFG->dirroot . '/search/tests/fixtures/mock_search_area.php'); + +/** + * Search engine base unit tests. + * + * @package core_search + * @copyright 2017 Matt Porritt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class search_base_testcase extends advanced_testcase { + /** + * @var \core_search::manager + */ + protected $search = null; + + /** + * @var Instace of core_search_generator. + */ + protected $generator = null; + + /** + * @var Instace of testable_engine. + */ + protected $engine = null; + + public function setUp() { + $this->resetAfterTest(); + set_config('enableglobalsearch', true); + + // Set \core_search::instance to the mock_search_engine as we don't require the search engine to be working to test this. + $search = testable_core_search::instance(); + + $this->generator = self::getDataGenerator()->get_plugin_generator('core_search'); + $this->generator->setup(); + } + + public function tearDown() { + // For unit tests before PHP 7, teardown is called even on skip. So only do our teardown if we did setup. + if ($this->generator) { + // Moodle DML freaks out if we don't teardown the temp table after each run. + $this->generator->teardown(); + $this->generator = null; + } + } + + /** + * Test base get search fileareas + */ + public function test_get_search_fileareas_base() { + + $builder = $this->getMockBuilder('\core_search\base'); + $builder->disableOriginalConstructor(); + $stub = $builder->getMockForAbstractClass(); + + $result = $stub->get_search_fileareas(); + + $this->assertEquals(array(), $result); + } + + /** + * Test base attach files + */ + public function test_attach_files_base() { + $filearea = 'search'; + $component = 'mod_test'; + + // Create file to add. + $fs = get_file_storage(); + $filerecord = array( + 'contextid' => 1, + 'component' => $component, + 'filearea' => $filearea, + 'itemid' => 1, + 'filepath' => '/', + 'filename' => 'testfile.txt'); + $content = 'All the news that\'s fit to print'; + $file = $fs->create_file_from_string($filerecord, $content); + + // Construct the search document. + $rec = new \stdClass(); + $area = new core_mocksearch\search\mock_search_area(); + $record = $this->generator->create_record($rec); + $document = $area->get_document($record); + + // Create a mock from the abstract class, + // with required methods stubbed. + $builder = $this->getMockBuilder('\core_search\base'); + $builder->disableOriginalConstructor(); + $builder->setMethods(array('get_search_fileareas', 'get_component_name')); + $stub = $builder->getMockForAbstractClass(); + $stub->method('get_search_fileareas')->willReturn(array($filearea)); + $stub->method('get_component_name')->willReturn($component); + + // Attach file to our test document. + $stub->attach_files($document); + + // Verify file is attached. + $files = $document->get_files(); + $file = array_values($files)[0]; + + $this->assertEquals(1, count($files)); + $this->assertEquals($content, $file->get_content()); + } +} diff --git a/user/classes/search/user.php b/user/classes/search/user.php index 1ede88bcda3..b4069b4b60e 100644 --- a/user/classes/search/user.php +++ b/user/classes/search/user.php @@ -120,4 +120,41 @@ class user extends \core_search\base { public function get_context_url(\core_search\document $doc) { return new \moodle_url('/user/profile.php', array('id' => $doc->get('itemid'))); } + + /** + * Returns true if this area uses file indexing. + * + * @return bool + */ + public function uses_file_indexing() { + return true; + } + + /** + * Return the context info required to index files for + * this search area. + * + * Should be onerridden by each search area. + * + * @return array + */ + public function get_search_fileareas() { + $fileareas = array( + 'profile' // Fileareas. + ); + + return $fileareas; + } + + /** + * Returns the moodle component name. + * + * It might be the plugin name (whole frankenstyle name) or the core subsystem name. + * + * @return string + */ + public function get_component_name() { + return 'user'; + } + }