mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 08:22:07 +02:00
MDL-53515 search: Extend search API to allow file indexing
This commit is contained in:
parent
fb75b07c9e
commit
091973dbd7
@ -64,6 +64,8 @@ $string['incourse'] = 'in course {$a}';
|
||||
$string['index'] = 'Index';
|
||||
$string['invalidindexerror'] = 'Index directory either contains an invalid index, or nothing at all.';
|
||||
$string['ittook'] = 'It took';
|
||||
$string['matchingfile'] = 'Matched from file <span class="filename">{$a}</span>';
|
||||
$string['matchingfiles'] = 'Matched from files:';
|
||||
$string['next'] = 'Next';
|
||||
$string['noindexmessage'] = 'Admin: There appears to be no search index. Please';
|
||||
$string['noresults'] = 'No results';
|
||||
|
@ -26,6 +26,8 @@ namespace mod_assign\search;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
||||
|
||||
/**
|
||||
* Search area for mod_assign activities.
|
||||
*
|
||||
@ -34,4 +36,32 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class activity extends \core_search\area\base_activity {
|
||||
/**
|
||||
* Returns true if this area uses file indexing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function uses_file_indexing() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the attached description files.
|
||||
*
|
||||
* @param document $document The current document
|
||||
* @return null
|
||||
*/
|
||||
public function attach_files($document) {
|
||||
$fs = get_file_storage();
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
131
mod/assign/tests/search_test.php
Normal file
131
mod/assign/tests/search_test.php
Normal file
@ -0,0 +1,131 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Assign search unit tests.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @category test
|
||||
* @copyright 2016 Eric Merrill {@link http://www.merrilldigital.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
|
||||
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
||||
|
||||
/**
|
||||
* Provides the unit tests for forum search.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @category test
|
||||
* @copyright 2016 Eric Merrill {@link http://www.merrilldigital.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_assign_search_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* @var string Area id
|
||||
*/
|
||||
protected $assignareaid = null;
|
||||
|
||||
public function setUp() {
|
||||
$this->resetAfterTest(true);
|
||||
set_config('enableglobalsearch', true);
|
||||
|
||||
$this->assignareaid = \core_search\manager::generate_areaid('mod_assign', 'activity');
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for assign file attachments.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_attach_files() {
|
||||
global $USER;
|
||||
|
||||
$this->setAdminUser();
|
||||
// Setup test data.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
$fs = get_file_storage();
|
||||
$usercontext = context_user::instance($USER->id);
|
||||
|
||||
$record = new stdClass();
|
||||
$record->course = $course->id;
|
||||
|
||||
$assign = $this->getDataGenerator()->create_module('assign', $record);
|
||||
$context = context_module::instance($assign->cmid);
|
||||
|
||||
// Attach the main file. We put them in the draft area, create_module will move them.
|
||||
$filerecord = array(
|
||||
'contextid' => $context->id,
|
||||
'component' => 'mod_assign',
|
||||
'filearea' => ASSIGN_INTROATTACHMENT_FILEAREA,
|
||||
'itemid' => 0,
|
||||
'filepath' => '/'
|
||||
);
|
||||
|
||||
// Attach 4 files.
|
||||
for ($i = 1; $i <= 4; $i++) {
|
||||
$filerecord['filename'] = 'myfile'.$i;
|
||||
$fs->create_file_from_string($filerecord, 'Test assign file '.$i);
|
||||
}
|
||||
|
||||
// And a fifth in a sub-folder.
|
||||
$filerecord['filename'] = 'myfile5';
|
||||
$filerecord['filepath'] = '/subfolder/';
|
||||
$fs->create_file_from_string($filerecord, 'Test assign file 5');
|
||||
|
||||
// Returns the instance as long as the area is supported.
|
||||
$searcharea = \core_search\manager::get_search_area($this->assignareaid);
|
||||
$this->assertInstanceOf('\mod_assign\search\activity', $searcharea);
|
||||
|
||||
$recordset = $searcharea->get_recordset_by_timestamp(0);
|
||||
$nrecords = 0;
|
||||
foreach ($recordset as $record) {
|
||||
$doc = $searcharea->get_document($record);
|
||||
$searcharea->attach_files($doc);
|
||||
$files = $doc->get_files();
|
||||
|
||||
// Assign should return all files attached.
|
||||
$this->assertCount(5, $files);
|
||||
|
||||
// We don't know the order, so get all the names, then sort, then check.
|
||||
$filenames = array();
|
||||
foreach ($files as $file) {
|
||||
$filenames[] = $file->get_filename();
|
||||
}
|
||||
sort($filenames);
|
||||
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$this->assertEquals('myfile'.$i, $filenames[($i - 1)]);
|
||||
}
|
||||
|
||||
$nrecords++;
|
||||
}
|
||||
|
||||
// If there would be an error/failure in the foreach above the recordset would be closed on shutdown.
|
||||
$recordset->close();
|
||||
$this->assertEquals(1, $nrecords);
|
||||
}
|
||||
|
||||
}
|
@ -34,4 +34,31 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class activity extends \core_search\area\base_activity {
|
||||
/**
|
||||
* Returns true if this area uses file indexing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function uses_file_indexing() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the folder files to the index.
|
||||
*
|
||||
* @param document $document The current document
|
||||
* @return null
|
||||
*/
|
||||
public function attach_files($document) {
|
||||
$fs = get_file_storage();
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
130
mod/folder/tests/search_test.php
Normal file
130
mod/folder/tests/search_test.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Folder search unit tests.
|
||||
*
|
||||
* @package mod_folder
|
||||
* @category test
|
||||
* @copyright 2016 Eric Merrill {@link http://www.merrilldigital.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
|
||||
|
||||
/**
|
||||
* Provides the unit tests for forum search.
|
||||
*
|
||||
* @package mod_folder
|
||||
* @category test
|
||||
* @copyright 2016 Eric Merrill {@link http://www.merrilldigital.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_folder_search_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* @var string Area id
|
||||
*/
|
||||
protected $folderareaid = null;
|
||||
|
||||
public function setUp() {
|
||||
$this->resetAfterTest(true);
|
||||
set_config('enableglobalsearch', true);
|
||||
|
||||
$this->folderareaid = \core_search\manager::generate_areaid('mod_folder', 'activity');
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for folder file attachments.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_attach_files() {
|
||||
global $USER;
|
||||
|
||||
$this->setAdminUser();
|
||||
// Setup test data.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
$fs = get_file_storage();
|
||||
$usercontext = context_user::instance($USER->id);
|
||||
|
||||
$record = new stdClass();
|
||||
$record->course = $course->id;
|
||||
$record->files = file_get_unused_draft_itemid();
|
||||
|
||||
// Attach the main file. We put them in the draft area, create_module will move them.
|
||||
$filerecord = array(
|
||||
'contextid' => $usercontext->id,
|
||||
'component' => 'user',
|
||||
'filearea' => 'draft',
|
||||
'itemid' => $record->files,
|
||||
'filepath' => '/'
|
||||
);
|
||||
|
||||
// Attach 4 files.
|
||||
for ($i = 1; $i <= 4; $i++) {
|
||||
$filerecord['filename'] = 'myfile'.$i;
|
||||
$fs->create_file_from_string($filerecord, 'Test folder file '.$i);
|
||||
}
|
||||
|
||||
// And a fifth in a sub-folder.
|
||||
$filerecord['filename'] = 'myfile5';
|
||||
$filerecord['filepath'] = '/subfolder/';
|
||||
$fs->create_file_from_string($filerecord, 'Test folder file 5');
|
||||
|
||||
$this->getDataGenerator()->create_module('folder', $record);
|
||||
|
||||
// Returns the instance as long as the area is supported.
|
||||
$searcharea = \core_search\manager::get_search_area($this->folderareaid);
|
||||
$this->assertInstanceOf('\mod_folder\search\activity', $searcharea);
|
||||
|
||||
$recordset = $searcharea->get_recordset_by_timestamp(0);
|
||||
$nrecords = 0;
|
||||
foreach ($recordset as $record) {
|
||||
$doc = $searcharea->get_document($record);
|
||||
$searcharea->attach_files($doc);
|
||||
$files = $doc->get_files();
|
||||
|
||||
// Folder should return all files attached.
|
||||
$this->assertCount(5, $files);
|
||||
|
||||
// We don't know the order, so get all the names, then sort, then check.
|
||||
$filenames = array();
|
||||
foreach ($files as $file) {
|
||||
$filenames[] = $file->get_filename();
|
||||
}
|
||||
sort($filenames);
|
||||
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$this->assertEquals('myfile'.$i, $filenames[($i - 1)]);
|
||||
}
|
||||
|
||||
$nrecords++;
|
||||
}
|
||||
|
||||
// If there would be an error/failure in the foreach above the recordset would be closed on shutdown.
|
||||
$recordset->close();
|
||||
$this->assertEquals(1, $nrecords);
|
||||
}
|
||||
|
||||
}
|
@ -65,7 +65,7 @@ class post extends \core_search\area\base_mod {
|
||||
FROM {forum_posts} fp
|
||||
JOIN {forum_discussions} fd ON fd.id = fp.discussion
|
||||
JOIN {forum} f ON f.id = fd.forum
|
||||
WHERE fp.modified >= ? ORDER BY fp.modified ASC';
|
||||
WHERE fp.modified >= ? ORDER BY fp.modified ASC';
|
||||
return $DB->get_recordset_sql($sql, array($modifiedfrom));
|
||||
}
|
||||
|
||||
@ -73,9 +73,10 @@ class post extends \core_search\area\base_mod {
|
||||
* Returns the document associated with this post id.
|
||||
*
|
||||
* @param stdClass $record Post info.
|
||||
* @param array $options
|
||||
* @return \core_search\document
|
||||
*/
|
||||
public function get_document($record) {
|
||||
public function get_document($record, $options = array()) {
|
||||
|
||||
try {
|
||||
$cm = $this->get_cm('forum', $record->forumid, $record->courseid);
|
||||
@ -96,15 +97,62 @@ class post extends \core_search\area\base_mod {
|
||||
$doc->set('title', $record->subject);
|
||||
$doc->set('content', content_to_text($record->message, $record->messageformat));
|
||||
$doc->set('contextid', $context->id);
|
||||
$doc->set('type', \core_search\manager::TYPE_TEXT);
|
||||
$doc->set('courseid', $record->courseid);
|
||||
$doc->set('userid', $record->userid);
|
||||
$doc->set('owneruserid', \core_search\manager::NO_OWNER_ID);
|
||||
$doc->set('modified', $record->modified);
|
||||
|
||||
// Check if this document should be considered new.
|
||||
if (isset($options['lastindexedtime']) && ($options['lastindexedtime'] < $record->created)) {
|
||||
// If the document was created after the last index time, it must be new.
|
||||
$doc->set_is_new(true);
|
||||
}
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this area uses file indexing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function uses_file_indexing() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the forum post attachments.
|
||||
*
|
||||
* @param document $document The current document
|
||||
* @return null
|
||||
*/
|
||||
public function attach_files($document) {
|
||||
global $DB;
|
||||
|
||||
$postid = $document->get('itemid');
|
||||
|
||||
try {
|
||||
$post = $this->get_post($postid);
|
||||
} catch (\dml_missing_record_exception $e) {
|
||||
unset($this->postsdata[$postid]);
|
||||
debugging('Could not get record to attach files to '.$document->get('id'), DEBUG_DEVELOPER);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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'));
|
||||
$context = \context_module::instance($cm->id);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user can access the document or not.
|
||||
*
|
||||
|
@ -267,4 +267,107 @@ class mod_forum_search_testcase extends advanced_testcase {
|
||||
$this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($discussion1reply1->id));
|
||||
$this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($discussion2reply1->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for post attachments.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_attach_files() {
|
||||
global $DB;
|
||||
|
||||
$fs = get_file_storage();
|
||||
|
||||
// Returns the instance as long as the area is supported.
|
||||
$searcharea = \core_search\manager::get_search_area($this->forumpostareaid);
|
||||
$this->assertInstanceOf('\mod_forum\search\post', $searcharea);
|
||||
|
||||
$user1 = self::getDataGenerator()->create_user();
|
||||
$user2 = self::getDataGenerator()->create_user();
|
||||
|
||||
$course1 = self::getDataGenerator()->create_course();
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student');
|
||||
$this->getDataGenerator()->enrol_user($user2->id, $course1->id, 'student');
|
||||
|
||||
$record = new stdClass();
|
||||
$record->course = $course1->id;
|
||||
|
||||
$forum1 = self::getDataGenerator()->create_module('forum', $record);
|
||||
|
||||
// Create discussion1.
|
||||
$record = new stdClass();
|
||||
$record->course = $course1->id;
|
||||
$record->userid = $user1->id;
|
||||
$record->forum = $forum1->id;
|
||||
$record->message = 'discussion';
|
||||
$record->attachemt = 1;
|
||||
$discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
|
||||
|
||||
// Attach 2 file to the discussion post.
|
||||
$post = $DB->get_record('forum_posts', array('discussion' => $discussion1->id));
|
||||
$filerecord = array(
|
||||
'contextid' => context_module::instance($forum1->cmid)->id,
|
||||
'component' => 'mod_forum',
|
||||
'filearea' => 'attachment',
|
||||
'itemid' => $post->id,
|
||||
'filepath' => '/',
|
||||
'filename' => 'myfile1'
|
||||
);
|
||||
$file1 = $fs->create_file_from_string($filerecord, 'Some contents 1');
|
||||
$filerecord['filename'] = 'myfile2';
|
||||
$file2 = $fs->create_file_from_string($filerecord, 'Some contents 2');
|
||||
|
||||
// Create post1 in discussion1.
|
||||
$record = new stdClass();
|
||||
$record->discussion = $discussion1->id;
|
||||
$record->parent = $discussion1->firstpost;
|
||||
$record->userid = $user2->id;
|
||||
$record->message = 'post2';
|
||||
$record->attachemt = 1;
|
||||
$discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
|
||||
|
||||
$filerecord['itemid'] = $discussion1reply1->id;
|
||||
$filerecord['filename'] = 'myfile3';
|
||||
$file3 = $fs->create_file_from_string($filerecord, 'Some contents 3');
|
||||
|
||||
// Create post2 in discussion1.
|
||||
$record = new stdClass();
|
||||
$record->discussion = $discussion1->id;
|
||||
$record->parent = $discussion1->firstpost;
|
||||
$record->userid = $user2->id;
|
||||
$record->message = 'post3';
|
||||
$discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
|
||||
|
||||
// Now get all the posts and see if they have the right files attached.
|
||||
$searcharea = \core_search\manager::get_search_area($this->forumpostareaid);
|
||||
$recordset = $searcharea->get_recordset_by_timestamp(0);
|
||||
$nrecords = 0;
|
||||
foreach ($recordset as $record) {
|
||||
$doc = $searcharea->get_document($record);
|
||||
$searcharea->attach_files($doc);
|
||||
$files = $doc->get_files();
|
||||
// Now check that each doc has the right files on it.
|
||||
switch ($doc->get('itemid')) {
|
||||
case ($post->id):
|
||||
$this->assertCount(2, $files);
|
||||
$this->assertEquals($file1->get_id(), $files[$file1->get_id()]->get_id());
|
||||
$this->assertEquals($file2->get_id(), $files[$file2->get_id()]->get_id());
|
||||
break;
|
||||
case ($discussion1reply1->id):
|
||||
$this->assertCount(1, $files);
|
||||
$this->assertEquals($file3->get_id(), $files[$file3->get_id()]->get_id());
|
||||
break;
|
||||
case ($discussion1reply2->id):
|
||||
$this->assertCount(0, $files);
|
||||
break;
|
||||
default:
|
||||
$this->fail('Unexpected post returned');
|
||||
break;
|
||||
}
|
||||
$nrecords++;
|
||||
}
|
||||
$recordset->close();
|
||||
$this->assertEquals(3, $nrecords);
|
||||
}
|
||||
}
|
||||
|
@ -61,9 +61,10 @@ class entry extends \core_search\area\base_mod {
|
||||
* Returns the documents associated with this glossary entry id.
|
||||
*
|
||||
* @param stdClass $entry glossary entry.
|
||||
* @param array $options
|
||||
* @return \core_search\document
|
||||
*/
|
||||
public function get_document($entry) {
|
||||
public function get_document($entry, $options = array()) {
|
||||
global $DB;
|
||||
|
||||
$keywords = array();
|
||||
@ -92,12 +93,17 @@ class entry extends \core_search\area\base_mod {
|
||||
$doc->set('title', $entry->concept);
|
||||
$doc->set('content', content_to_text($entry->definition, $entry->definitionformat));
|
||||
$doc->set('contextid', $context->id);
|
||||
$doc->set('type', \core_search\manager::TYPE_TEXT);
|
||||
$doc->set('courseid', $entry->course);
|
||||
$doc->set('userid', $entry->userid);
|
||||
$doc->set('owneruserid', \core_search\manager::NO_OWNER_ID);
|
||||
$doc->set('modified', $entry->timemodified);
|
||||
|
||||
// Check if this document should be considered new.
|
||||
if (isset($options['lastindexedtime']) && ($options['lastindexedtime'] < $entry->timecreated)) {
|
||||
// If the document was created after the last index time, it must be new.
|
||||
$doc->set_is_new(true);
|
||||
}
|
||||
|
||||
// Adding keywords as extra info.
|
||||
if ($keywords) {
|
||||
$doc->set('description1', implode(' ' , $keywords));
|
||||
|
@ -43,9 +43,10 @@ class activity extends \core_search\area\base_activity {
|
||||
* description field is not.
|
||||
*
|
||||
* @param stdClass $record
|
||||
* @param array $options
|
||||
* @return \core_search\document
|
||||
*/
|
||||
public function get_document($record) {
|
||||
public function get_document($record, $options = array()) {
|
||||
|
||||
try {
|
||||
$cm = $this->get_cm($this->get_module_name(), $record->id, $record->course);
|
||||
@ -66,7 +67,6 @@ class activity extends \core_search\area\base_activity {
|
||||
$doc->set('title', $record->name);
|
||||
$doc->set('content', content_to_text($record->content, $record->contentformat));
|
||||
$doc->set('contextid', $context->id);
|
||||
$doc->set('type', \core_search\manager::TYPE_TEXT);
|
||||
$doc->set('courseid', $record->course);
|
||||
$doc->set('owneruserid', \core_search\manager::NO_OWNER_ID);
|
||||
$doc->set('modified', $record->timemodified);
|
||||
|
@ -34,4 +34,34 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class activity extends \core_search\area\base_activity {
|
||||
/**
|
||||
* Returns true if this area uses file indexing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function uses_file_indexing() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the main file to the index.
|
||||
*
|
||||
* @param document $document The current document
|
||||
* @return null
|
||||
*/
|
||||
public function attach_files($document) {
|
||||
$fs = get_file_storage();
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
116
mod/resource/tests/search_test.php
Normal file
116
mod/resource/tests/search_test.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Resource search unit tests.
|
||||
*
|
||||
* @package mod_resource
|
||||
* @category test
|
||||
* @copyright 2016 Eric Merrill {@link http://www.merrilldigital.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
|
||||
|
||||
/**
|
||||
* Provides the unit tests for forum search.
|
||||
*
|
||||
* @package mod_resource
|
||||
* @category test
|
||||
* @copyright 2016 Eric Merrill {@link http://www.merrilldigital.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_resource_search_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* @var string Area id
|
||||
*/
|
||||
protected $resourceareaid = null;
|
||||
|
||||
public function setUp() {
|
||||
$this->resetAfterTest(true);
|
||||
set_config('enableglobalsearch', true);
|
||||
|
||||
$this->resourceareaid = \core_search\manager::generate_areaid('mod_resource', 'activity');
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for resource file attachments.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_attach_files() {
|
||||
global $USER;
|
||||
|
||||
$this->setAdminUser();
|
||||
// Setup test data.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
$fs = get_file_storage();
|
||||
$usercontext = context_user::instance($USER->id);
|
||||
|
||||
$record = new stdClass();
|
||||
$record->course = $course->id;
|
||||
$record->files = file_get_unused_draft_itemid();
|
||||
|
||||
// Attach the main file. We put them in the draft area, create_module will move them.
|
||||
$filerecord = array(
|
||||
'contextid' => $usercontext->id,
|
||||
'component' => 'user',
|
||||
'filearea' => 'draft',
|
||||
'itemid' => $record->files,
|
||||
'filepath' => '/',
|
||||
'filename' => 'mainfile',
|
||||
'sortorder' => 1
|
||||
);
|
||||
$fs->create_file_from_string($filerecord, 'Test resource file');
|
||||
|
||||
// Attach a second file that shouldn't be returned with the search doc.
|
||||
$filerecord['filename'] = 'extrafile';
|
||||
$filerecord['sortorder'] = 0;
|
||||
$fs->create_file_from_string($filerecord, 'Test resource file 2');
|
||||
|
||||
$resource = $this->getDataGenerator()->create_module('resource', $record);
|
||||
|
||||
$searcharea = \core_search\manager::get_search_area($this->resourceareaid);
|
||||
$this->assertInstanceOf('\mod_resource\search\activity', $searcharea);
|
||||
|
||||
$recordset = $searcharea->get_recordset_by_timestamp(0);
|
||||
$nrecords = 0;
|
||||
foreach ($recordset as $record) {
|
||||
$doc = $searcharea->get_document($record);
|
||||
$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());
|
||||
|
||||
$nrecords++;
|
||||
}
|
||||
|
||||
$recordset->close();
|
||||
$this->assertEquals(1, $nrecords);
|
||||
}
|
||||
|
||||
}
|
@ -41,10 +41,11 @@ class activity extends \core_search\area\base_activity {
|
||||
* Overwrites base_activity to add the provided URL as description.
|
||||
*
|
||||
* @param stdClass $record
|
||||
* @param array $options
|
||||
* @return \core_search\document
|
||||
*/
|
||||
public function get_document($record) {
|
||||
$doc = parent::get_document($record);
|
||||
public function get_document($record, $options = array()) {
|
||||
$doc = parent::get_document($record, $options);
|
||||
if (!$doc) {
|
||||
return false;
|
||||
}
|
||||
|
@ -185,6 +185,15 @@ abstract class base {
|
||||
return (bool)get_config($componentname, $varname . '_enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this area uses file indexing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function uses_file_indexing() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recordset ordered by modification date ASC.
|
||||
*
|
||||
@ -213,10 +222,25 @@ abstract class base {
|
||||
* Search areas should send plain text to the search engine, use the following function to convert any user
|
||||
* input data to plain text: {@link content_to_text}
|
||||
*
|
||||
* Valid keys for the options array are:
|
||||
* indexfiles => File indexing is enabled if true.
|
||||
* lastindexedtime => The last time this area was indexed. 0 if never indexed.
|
||||
*
|
||||
* @param \stdClass $record A record containing, at least, the indexed document id and a modified timestamp
|
||||
* @param array $options Options for document creation
|
||||
* @return \core_search\document
|
||||
*/
|
||||
abstract public function get_document($record);
|
||||
abstract public function get_document($record, $options = array());
|
||||
|
||||
/**
|
||||
* Add any files to the document that should be indexed.
|
||||
*
|
||||
* @param document $document The current document
|
||||
* @return void
|
||||
*/
|
||||
public function attach_files($document) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the current user see the document.
|
||||
|
@ -43,6 +43,11 @@ abstract class base_activity extends base_mod {
|
||||
*/
|
||||
const MODIFIED_FIELD_NAME = 'timemodified';
|
||||
|
||||
/**
|
||||
* Activities with a time created field can overwrite this constant.
|
||||
*/
|
||||
const CREATED_FIELD_NAME = '';
|
||||
|
||||
/**
|
||||
* The context levels the search area is working on.
|
||||
* @var array
|
||||
@ -68,9 +73,10 @@ abstract class base_activity extends base_mod {
|
||||
* default ones, or to fill description optional fields with extra stuff.
|
||||
*
|
||||
* @param stdClass $record
|
||||
* @param array $options
|
||||
* @return \core_search\document
|
||||
*/
|
||||
public function get_document($record) {
|
||||
public function get_document($record, $options = array()) {
|
||||
|
||||
try {
|
||||
$cm = $this->get_cm($this->get_module_name(), $record->id, $record->course);
|
||||
@ -91,11 +97,19 @@ abstract class base_activity extends base_mod {
|
||||
$doc->set('title', $record->name);
|
||||
$doc->set('content', content_to_text($record->intro, $record->introformat));
|
||||
$doc->set('contextid', $context->id);
|
||||
$doc->set('type', \core_search\manager::TYPE_TEXT);
|
||||
$doc->set('courseid', $record->course);
|
||||
$doc->set('owneruserid', \core_search\manager::NO_OWNER_ID);
|
||||
$doc->set('modified', $record->{static::MODIFIED_FIELD_NAME});
|
||||
|
||||
// Check if this document should be considered new.
|
||||
if (isset($options['lastindexedtime'])) {
|
||||
$createdfield = static::CREATED_FIELD_NAME;
|
||||
if (!empty($createdfield) && ($options['lastindexedtime'] < $record->{$createdfield})) {
|
||||
// If the document was created after the last index time, it must be new.
|
||||
$doc->set_is_new(true);
|
||||
}
|
||||
}
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
|
@ -63,13 +63,12 @@ abstract class base_mod extends base {
|
||||
* @return \cm_info
|
||||
*/
|
||||
protected function get_cm($modulename, $instanceid, $courseid) {
|
||||
|
||||
$modinfo = get_fast_modinfo($courseid);
|
||||
|
||||
// Hopefully not many, they are indexed by cmid.
|
||||
$instances = $modinfo->get_instances_of($modulename);
|
||||
foreach ($instances as $cminfo) {
|
||||
if ($cminfo->instance === $instanceid) {
|
||||
if ($cminfo->instance == $instanceid) {
|
||||
return $cminfo;
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,16 @@ class document implements \renderable, \templatable {
|
||||
*/
|
||||
protected $contentitemid = null;
|
||||
|
||||
/**
|
||||
* @var bool Should be set to true if document hasn't been indexed before. False if unknown.
|
||||
*/
|
||||
protected $isnew = false;
|
||||
|
||||
/**
|
||||
* @var \stored_file[] An array of stored files to attach to the document.
|
||||
*/
|
||||
protected $files = array();
|
||||
|
||||
/**
|
||||
* All required fields any doc should contain.
|
||||
*
|
||||
@ -156,9 +166,21 @@ class document implements \renderable, \templatable {
|
||||
'type' => 'string',
|
||||
'stored' => true,
|
||||
'indexed' => true
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Any fields that are engine specifc. These are fields that are solely used by a search engine plugin
|
||||
* for internal purposes.
|
||||
*
|
||||
* Field names should be prefixed with engine name to avoid potential conflict with core fields.
|
||||
*
|
||||
* Uses same format as fields above.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $enginefields = array();
|
||||
|
||||
/**
|
||||
* We ensure that the document has a unique id across search areas.
|
||||
*
|
||||
@ -178,6 +200,40 @@ class document implements \renderable, \templatable {
|
||||
$this->data['itemid'] = intval($itemid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a stored file to the document.
|
||||
*
|
||||
* @param \stored_file|int $file The file to add, or file id.
|
||||
* @return void
|
||||
*/
|
||||
public function add_stored_file($file) {
|
||||
if (is_numeric($file)) {
|
||||
$this->files[$file] = $file;
|
||||
} else {
|
||||
$this->files[$file->get_id()] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of attached files.
|
||||
*
|
||||
* @return \stored_file[]
|
||||
*/
|
||||
public function get_files() {
|
||||
// The files array can contain stored file ids, so we need to get instances if asked.
|
||||
foreach ($this->files as $id => $listfile) {
|
||||
if (is_numeric($listfile)) {
|
||||
$fs = get_file_storage();
|
||||
|
||||
if ($file = $fs->get_file_by_id($id)) {
|
||||
$this->files[$id] = $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter.
|
||||
*
|
||||
@ -197,6 +253,8 @@ class document implements \renderable, \templatable {
|
||||
$fielddata = static::$requiredfields[$fieldname];
|
||||
} else if (!empty(static::$optionalfields[$fieldname])) {
|
||||
$fielddata = static::$optionalfields[$fieldname];
|
||||
} else if (!empty(static::$enginefields[$fieldname])) {
|
||||
$fielddata = static::$enginefields[$fieldname];
|
||||
}
|
||||
|
||||
if (empty($fielddata)) {
|
||||
@ -268,13 +326,31 @@ class document implements \renderable, \templatable {
|
||||
return (isset($this->data[$field]) || isset($this->extradata[$field]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if this is a new document. False if unknown.
|
||||
*
|
||||
* @param bool $new
|
||||
*/
|
||||
public function set_is_new($new) {
|
||||
$this->isnew = (bool)$new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the document is new. False if unknown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_is_new() {
|
||||
return $this->isnew;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all default fields definitions.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_default_fields_definition() {
|
||||
return static::$requiredfields + static::$optionalfields;
|
||||
return static::$requiredfields + static::$optionalfields + static::$enginefields;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -337,7 +413,7 @@ class document implements \renderable, \templatable {
|
||||
* @return void
|
||||
*/
|
||||
public function set_data_from_engine($docdata) {
|
||||
$fields = static::$requiredfields + static::$optionalfields;
|
||||
$fields = static::$requiredfields + static::$optionalfields + static::$enginefields;
|
||||
foreach ($fields as $fieldname => $field) {
|
||||
|
||||
// Optional params might not be there.
|
||||
@ -395,6 +471,8 @@ class document implements \renderable, \templatable {
|
||||
* @return array
|
||||
*/
|
||||
public function export_for_engine() {
|
||||
// Set any unset defaults.
|
||||
$this->apply_defaults();
|
||||
|
||||
// We don't want to affect the document instance.
|
||||
$data = $this->data;
|
||||
@ -416,7 +494,8 @@ class document implements \renderable, \templatable {
|
||||
}
|
||||
}
|
||||
|
||||
foreach (static::$optionalfields as $fieldname => $field) {
|
||||
$fields = static::$optionalfields + static::$enginefields;
|
||||
foreach ($fields as $fieldname => $field) {
|
||||
if (!isset($data[$fieldname])) {
|
||||
continue;
|
||||
}
|
||||
@ -432,6 +511,18 @@ class document implements \renderable, \templatable {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any defaults to unset fields before export. Called after document building, but before export.
|
||||
*
|
||||
* Sub-classes of this should make sure to call parent::apply_defaults().
|
||||
*/
|
||||
protected function apply_defaults() {
|
||||
// Set the default type, TYPE_TEXT.
|
||||
if (!isset($this->data['type'])) {
|
||||
$this->data['type'] = manager::TYPE_TEXT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the document data to be used as a template context.
|
||||
*
|
||||
@ -459,6 +550,22 @@ class document implements \renderable, \templatable {
|
||||
'description2' => $this->is_set('description2') ? $this->format_text($this->get('description2')) : null,
|
||||
];
|
||||
|
||||
// Now take any attached any files.
|
||||
$files = $this->get_files();
|
||||
if (!empty($files)) {
|
||||
if (count($files) > 1) {
|
||||
$filenames = array();
|
||||
foreach ($files as $file) {
|
||||
$filenames[] = $file->get_filename();
|
||||
}
|
||||
$data['multiplefiles'] = true;
|
||||
$data['filenames'] = $filenames;
|
||||
} else {
|
||||
$file = reset($files);
|
||||
$data['filename'] = $file->get_filename();
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->is_set('userid')) {
|
||||
$data['userurl'] = new \moodle_url('/user/view.php', array('id' => $this->get('userid'), 'course' => $this->get('courseid')));
|
||||
$data['userfullname'] = format_string($this->get('userfullname'), true, array('context' => $this->get('contextid')));
|
||||
|
@ -313,6 +313,15 @@ abstract class engine {
|
||||
return $this->queryerror;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if file indexing is supported and enabled. False otherwise.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function file_indexing_enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the current query error value.
|
||||
*
|
||||
@ -334,10 +343,11 @@ abstract class engine {
|
||||
/**
|
||||
* Adds a document to the search engine.
|
||||
*
|
||||
* @param array $doc
|
||||
* @return void
|
||||
* @param document $document
|
||||
* @param bool $fileindexing True if file indexing is to be used
|
||||
* @return bool False if the file was skipped or failed, true on success
|
||||
*/
|
||||
abstract function add_document($doc);
|
||||
abstract function add_document($document, $fileindexing = false);
|
||||
|
||||
/**
|
||||
* Executes the query on the engine.
|
||||
|
@ -42,6 +42,11 @@ class manager {
|
||||
*/
|
||||
const TYPE_TEXT = 1;
|
||||
|
||||
/**
|
||||
* @var int File contents.
|
||||
*/
|
||||
const TYPE_FILE = 2;
|
||||
|
||||
/**
|
||||
* @var int User can not access the document.
|
||||
*/
|
||||
@ -498,33 +503,40 @@ class manager {
|
||||
$numdocsignored = 0;
|
||||
$lastindexeddoc = 0;
|
||||
|
||||
$prevtimestart = intval(get_config($componentconfigname, $varname . '_indexingstart'));
|
||||
|
||||
if ($fullindex === true) {
|
||||
$prevtimestart = 0;
|
||||
$referencestarttime = 0;
|
||||
} else {
|
||||
$prevtimestart = intval(get_config($componentconfigname, $varname . '_indexingstart'));
|
||||
$referencestarttime = $prevtimestart;
|
||||
}
|
||||
|
||||
// Getting the recordset from the area.
|
||||
$recordset = $searcharea->get_recordset_by_timestamp($prevtimestart);
|
||||
$recordset = $searcharea->get_recordset_by_timestamp($referencestarttime);
|
||||
|
||||
// Pass get_document as callback.
|
||||
$iterator = new \core\dml\recordset_walk($recordset, array($searcharea, 'get_document'));
|
||||
$fileindexing = $this->engine->file_indexing_enabled() && $searcharea->uses_file_indexing();
|
||||
$options = array('indexfiles' => $fileindexing, 'lastindexedtime' => $prevtimestart);
|
||||
$iterator = new \core\dml\recordset_walk($recordset, array($searcharea, 'get_document'), $options);
|
||||
foreach ($iterator as $document) {
|
||||
|
||||
if (!$document instanceof \core_search\document) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$docdata = $document->export_for_engine();
|
||||
switch ($docdata['type']) {
|
||||
case static::TYPE_TEXT:
|
||||
$this->engine->add_document($docdata);
|
||||
$numdocs++;
|
||||
break;
|
||||
default:
|
||||
$numdocsignored++;
|
||||
$iterator->close();
|
||||
throw new \moodle_exception('doctypenotsupported', 'search');
|
||||
if ($prevtimestart == 0) {
|
||||
// If we have never indexed this area before, it must be new.
|
||||
$document->set_is_new(true);
|
||||
}
|
||||
|
||||
if ($fileindexing) {
|
||||
// Attach files if we are indexing.
|
||||
$searcharea->attach_files($document);
|
||||
}
|
||||
|
||||
if ($this->engine->add_document($document, $fileindexing)) {
|
||||
$numdocs++;
|
||||
} else {
|
||||
$numdocsignored++;
|
||||
}
|
||||
|
||||
$lastindexeddoc = $document->get('modified');
|
||||
|
@ -314,11 +314,27 @@ class engine extends \core_search\engine {
|
||||
*
|
||||
* This does not commit to the search engine.
|
||||
*
|
||||
* @param array $doc
|
||||
* @return void
|
||||
* @param document $document
|
||||
* @param bool $fileindexing True if file indexing is to be used
|
||||
* @return bool
|
||||
*/
|
||||
public function add_document($doc) {
|
||||
public function add_document($document, $fileindexing = false) {
|
||||
$docdata = $document->export_for_engine();
|
||||
|
||||
if (!$this->add_text_document($docdata)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a text document to the search engine.
|
||||
*
|
||||
* @param array $filedoc
|
||||
* @return bool
|
||||
*/
|
||||
protected function add_text_document($doc) {
|
||||
$solrdoc = new \SolrInputDocument();
|
||||
foreach ($doc as $field => $value) {
|
||||
$solrdoc->addField($field, $value);
|
||||
@ -326,6 +342,7 @@ class engine extends \core_search\engine {
|
||||
|
||||
try {
|
||||
$result = $this->get_search_client()->addDocument($solrdoc, true, static::AUTOCOMMIT_WITHIN);
|
||||
return true;
|
||||
} catch (\SolrClientException $e) {
|
||||
debugging('Solr client error adding document with id ' . $doc['id'] . ': ' . $e->getMessage(), DEBUG_DEVELOPER);
|
||||
} catch (\SolrServerException $e) {
|
||||
@ -333,6 +350,8 @@ class engine extends \core_search\engine {
|
||||
$msg = strtok($e->getMessage(), "\n");
|
||||
debugging('Solr server error adding document with id ' . $doc['id'] . ': ' . $msg, DEBUG_DEVELOPER);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -213,7 +213,7 @@ class search_solr_engine_testcase extends advanced_testcase {
|
||||
|
||||
// Get the doc and insert the default doc.
|
||||
$doc = $area->get_document($record);
|
||||
$engine->add_document($doc->export_for_engine());
|
||||
$engine->add_document($doc);
|
||||
|
||||
$users = array();
|
||||
$users[] = $this->getDataGenerator()->create_user();
|
||||
@ -225,9 +225,10 @@ class search_solr_engine_testcase extends advanced_testcase {
|
||||
|
||||
// Now add a custom doc for each user.
|
||||
foreach ($users as $user) {
|
||||
$doc = $area->get_document($record);
|
||||
$doc->set('id', $originalid.'-'.$user->id);
|
||||
$doc->set('owneruserid', $user->id);
|
||||
$engine->add_document($doc->export_for_engine());
|
||||
$engine->add_document($doc);
|
||||
}
|
||||
|
||||
$engine->area_index_complete($area->get_area_id());
|
||||
|
@ -38,6 +38,9 @@
|
||||
* userfullname
|
||||
* description1
|
||||
* description2
|
||||
* filename
|
||||
* multiplefiles
|
||||
* filenames
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
@ -64,6 +67,21 @@
|
||||
{{#description2}}
|
||||
<div class="result-content">{{{description2}}}</div>
|
||||
{{/description2}}
|
||||
{{#filename}}
|
||||
<div class="result-content-filename">
|
||||
{{#str}}matchingfile, search, {{filename}}{{/str}}
|
||||
</div>
|
||||
{{/filename}}
|
||||
{{#multiplefiles}}
|
||||
<div class="result-content-filenames">
|
||||
{{#str}}matchingfiles, search{{/str}}<br>
|
||||
<ul class="list">
|
||||
{{#filenames}}
|
||||
<li><span class="filename">{{.}}</span></li>
|
||||
{{/filenames}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/multiplefiles}}
|
||||
<div class="result-context-info">
|
||||
<a href="{{{contexturl}}}">{{#str}}viewresultincontext, search{{/str}}</a> -
|
||||
<a href="{{{courseurl}}}">{{#str}}incourse, search, {{coursefullname}}{{/str}}</a>
|
||||
|
27
search/tests/fixtures/mock_search_area.php
vendored
27
search/tests/fixtures/mock_search_area.php
vendored
@ -44,7 +44,7 @@ class role_capabilities extends \core_search\area\base {
|
||||
return $DB->get_recordset_sql("SELECT id, contextid, roleid, capability FROM {role_capabilities} where timemodified >= ? and capability = ?", array($modifiedfrom, 'moodle/course:renameroles'));
|
||||
}
|
||||
|
||||
public function get_document($record) {
|
||||
public function get_document($record, $options = array()) {
|
||||
global $USER;
|
||||
|
||||
// Prepare associative array with data from DB.
|
||||
@ -52,7 +52,6 @@ class role_capabilities extends \core_search\area\base {
|
||||
$doc->set('title', $record->capability . ' roleid ' . $record->roleid);
|
||||
$doc->set('content', $record->capability . ' roleid ' . $record->roleid . ' message');
|
||||
$doc->set('contextid', $record->contextid);
|
||||
$doc->set('type', \core_search\manager::TYPE_TEXT);
|
||||
$doc->set('courseid', SITEID);
|
||||
$doc->set('userid', $USER->id);
|
||||
$doc->set('owneruserid', \core_search\manager::NO_OWNER_ID);
|
||||
@ -61,6 +60,30 @@ class role_capabilities extends \core_search\area\base {
|
||||
return $doc;
|
||||
}
|
||||
|
||||
public function attach_files($document) {
|
||||
global $CFG;
|
||||
|
||||
// Add the searchable file fixture.
|
||||
$syscontext = \context_system::instance();
|
||||
$filerecord = array(
|
||||
'contextid' => $syscontext->id,
|
||||
'component' => 'core',
|
||||
'filearea' => 'unittest',
|
||||
'itemid' => 0,
|
||||
'filepath' => '/',
|
||||
'filename' => 'searchfile'.$document->get('itemid').'.txt',
|
||||
);
|
||||
|
||||
$fs = get_file_storage();
|
||||
$file = $fs->create_file_from_string($filerecord, 'File contents');
|
||||
|
||||
$document->add_stored_file($file);
|
||||
}
|
||||
|
||||
public function uses_file_indexing() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function check_access($id) {
|
||||
return \core_search\manager::ACCESS_GRANTED;
|
||||
}
|
||||
|
2
search/tests/fixtures/mock_search_engine.php
vendored
2
search/tests/fixtures/mock_search_engine.php
vendored
@ -37,7 +37,7 @@ class engine extends \core_search\engine {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function add_document($doc) {
|
||||
public function add_document($document, $fileindexing = false) {
|
||||
// No need to implement.
|
||||
}
|
||||
|
||||
|
@ -12,3 +12,6 @@
|
||||
margin: 7px 0;
|
||||
}
|
||||
|
||||
.search-results .result .filename {
|
||||
font-style: italic;
|
||||
}
|
||||
|
@ -12,6 +12,10 @@
|
||||
margin: 7px 0;
|
||||
}
|
||||
|
||||
.search-results .result .filename {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.search-input-wrapper {
|
||||
margin: 0 5px 0 2px;
|
||||
overflow: hidden;
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user