Skip to content
Permalink
Browse files

MDL-53354 search: Add ability to limit search doc to a user

  • Loading branch information...
merrill-oakland committed Mar 10, 2016
1 parent 7adc7ef commit f6b425e2de08b68b19dadfd7569d082a9528d7e9
@@ -99,6 +99,7 @@ public function get_document($record) {
$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);
return $doc;
@@ -95,6 +95,7 @@ public function get_document($entry) {
$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);
// Adding keywords as extra info.
@@ -68,6 +68,7 @@ public function get_document($record) {
$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);
$doc->set('description1', content_to_text($record->intro, $record->introformat));
@@ -93,6 +93,7 @@ public function get_document($record) {
$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});
return $doc;
@@ -120,6 +120,11 @@ class document implements \renderable, \templatable {
'stored' => true,
'indexed' => false
),
'owneruserid' => array(
'type' => 'int',
'stored' => true,
'indexed' => true
),
'modified' => array(
'type' => 'tdate',
'stored' => true,
@@ -300,7 +300,7 @@ public function clear_query_error() {
* Executes the query on the engine.
*
* Implementations of this function should check user context array to limit the results to contexts where the
* user have access.
* user have access. They should also limit the owneruserid field to manger::NO_OWNER_ID or the current user's id.
*
* @param stdClass $filters Query and filters to apply.
* @param array $usercontexts Contexts where the user has access. True if the user can access all contexts.
@@ -67,6 +67,11 @@ class manager {
*/
const DISPLAY_RESULTS_PER_PAGE = 10;
/**
* @var int The id to be placed in owneruserid when there is no owner.
*/
const NO_OWNER_ID = 0;
/**
* @var \core_search\area\base[] Enabled search areas.
*/
@@ -398,12 +403,13 @@ protected function get_areas_user_accesses() {
* @return \core_search\document[]
*/
public function search(\stdClass $formdata) {
global $USER;
$cache = \cache::make('core', 'search_results');
// Generate a string from all query filters
// Not including $areascontext here, being a user cache it is not needed.
$querykey = $this->generate_query_key($formdata);
$querykey = $this->generate_query_key($formdata, $USER->id);
// Look for cached results before executing it.
if ($results = $cache->get($querykey)) {
@@ -434,6 +440,7 @@ public function search(\stdClass $formdata) {
* @return string
*/
protected function generate_query_key($formdata) {
global $USER;
// Empty values by default (although q should always have a value).
$fields = array('q', 'title', 'areaid', 'timestart', 'timeend', 'page');
@@ -449,7 +456,7 @@ protected function generate_query_key($formdata) {
// Although it is not likely, we prevent cache hits if available search areas change during the session.
$enabledareas = implode('-', array_keys(static::get_search_areas_list(true)));
return md5($params->q . 'title=' . $params->title . 'areaid=' . $params->areaid .
return md5($params->q . 'userid=' . $USER->id . 'title=' . $params->title . 'areaid=' . $params->areaid .
'timestart=' . $params->timestart . 'timeend=' . $params->timeend . 'page=' . $params->page .
$enabledareas);
}
@@ -64,11 +64,12 @@ class engine extends \core_search\engine {
* Prepares a Solr query, applies filters and executes it returning its results.
*
* @throws \core_search\engine_exception
* @param stdClass $filters Containing query and filters.
* @param array $usercontexts Contexts where the user has access. True if the user can access all contexts.
* @param stdClass $filters Containing query and filters.
* @param array $usercontexts Contexts where the user has access. True if the user can access all contexts.
* @return \core_search\document[] Results or false if no results
*/
public function execute_query($filters, $usercontexts) {
global $USER;
// Let's keep these changes internal.
$data = clone $filters;
@@ -111,6 +112,9 @@ public function execute_query($filters, $usercontexts) {
$query->addFilterQuery('{!cache=false}modified:[' . $data->timestart . ' TO ' . $data->timeend . ']');
}
// Restrict to users who are supposed to be able to see a particular result.
$query->addFilterQuery('owneruserid:(' . \core_search\manager::NO_OWNER_ID . ' OR ' . $USER->id . ')');
// And finally restrict it to the context where the user can access, we want this one cached.
// If the user can access all contexts $usercontexts value is just true, we don't need to filter
// in that case.
@@ -224,6 +228,10 @@ public function merge_highlight_field_values($doc, $highlighteddoc) {
* @return array $results containing final results to be displayed.
*/
public function query_response($queryresponse) {
global $USER;
$userid = $USER->id;
$noownerid = \core_search\manager::NO_OWNER_ID;
$response = $queryresponse->getResponse();
$numgranted = 0;
@@ -237,6 +245,12 @@ public function query_response($queryresponse) {
// Iterate through the results checking its availability and whether they are available for the user or not.
foreach ($docs as $key => $docdata) {
if ($docdata['owneruserid'] != $noownerid && $docdata['owneruserid'] != $userid) {
// If owneruserid is set, no other user should be able to access this record.
unset($docs[$key]);
continue;
}
if (!$searcharea = $this->get_search_area($docdata->areaid)) {
unset($docs[$key]);
continue;
@@ -176,4 +176,80 @@ public function test_delete() {
cache_helper::purge_by_definition('core', 'search_results');
$this->assertCount(0, $this->search->search($querydata));
}
public function test_alloweduserid() {
$engine = $this->search->get_engine();
$area = new core_mocksearch\search\role_capabilities();
// Get the first record for the recordset.
$recordset = $area->get_recordset_by_timestamp();
foreach ($recordset as $r) {
$record = $r;
break;
}
$recordset->close();
// Get the doc and insert the default doc.
$doc = $area->get_document($record);
$engine->add_document($doc->export_for_engine());
$users = array();
$users[] = $this->getDataGenerator()->create_user();
$users[] = $this->getDataGenerator()->create_user();
$users[] = $this->getDataGenerator()->create_user();
// Add a record that only user 100 can see.
$originalid = $doc->get('id');
// Now add a custom doc for each user.
foreach ($users as $user) {
$doc->set('id', $originalid.'-'.$user->id);
$doc->set('owneruserid', $user->id);
$engine->add_document($doc->export_for_engine());
}
$engine->commit();
$querydata = new stdClass();
$querydata->q = 'message';
$querydata->title = $doc->get('title');
// We are going to go through each user and see if they get the original and the owned doc.
foreach ($users as $user) {
$this->setUser($user);
$results = $this->search->search($querydata);
$this->assertCount(2, $results);
$owned = 0;
$notowned = 0;
// We don't know what order we will get the results in, so we are doing this.
foreach ($results as $result) {
$owneruserid = $result->get('owneruserid');
if (empty($owneruserid)) {
$notowned++;
$this->assertEquals(0, $owneruserid);
$this->assertEquals($originalid, $result->get('id'));
} else {
$owned++;
$this->assertEquals($user->id, $owneruserid);
$this->assertEquals($originalid.'-'.$user->id, $result->get('id'));
}
}
$this->assertEquals(1, $owned);
$this->assertEquals(1, $notowned);
}
// Now test a user with no owned results.
$otheruser = $this->getDataGenerator()->create_user();
$this->setUser($otheruser);
$results = $this->search->search($querydata);
$this->assertCount(1, $results);
$this->assertEquals(0, $results[0]->get('owneruserid'));
$this->assertEquals($originalid, $results[0]->get('id'));
}
}
@@ -55,6 +55,7 @@ public function get_document($record) {
$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);
$doc->set('modified', time());
return $doc;

0 comments on commit f6b425e

Please sign in to comment.
You can’t perform that action at this time.