3 * Global Search Engine for Moodle
4 * add-on 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
7 * document handling for data activity module
8 * This file contains the mapping between a database object and it's indexable counterpart,
10 * Functions for iterating and retrieving the necessary records are now also included
11 * in this file, rather than mod/data/lib.php
14 require_once("$CFG->dirroot/search/documents/document.php");
15 require_once("$CFG->dirroot/mod/data/lib.php");
18 * a class for representing searchable information (data records)
21 class DataSearchDocument
extends SearchDocument
{
27 public function __construct(&$record, $course_id, $context_id) {
28 // generic information; required
29 $doc->docid
= $record['id'];
30 $doc->documenttype
= SEARCH_TYPE_DATA
;
31 $doc->itemtype
= 'record';
32 $doc->contextid
= $context_id;
34 $doc->title
= $record['title'];
35 $doc->date
= $record['timemodified'];
36 //remove '(ip.ip.ip.ip)' from data record author field
37 if ($record['userid']){
38 $user = get_record('user', 'id', $record['userid']);
40 $doc->author
= (isset($user)) ?
$user->firstname
.' '.$user->lastname
: '' ;
41 $doc->contents
= $record['content'];
42 $doc->url
= data_make_link($record['dataid'], $record['id']);
44 // module specific information; optional
45 // $data->params = serialize(@$record['params']); may be useful
46 $data->database
= $record['dataid'];
48 // construct the parent class
49 parent
::__construct($doc, $data, $course_id, $record['groupid'], $record['userid'], PATH_FOR_SEARCH_TYPE_DATA
);
51 } //ChatSearchDocument
54 * a class for representing searchable information (comments on data records)
57 class DataCommentSearchDocument
extends SearchDocument
{
63 public function __construct(&$comment, $course_id, $context_id) {
64 // generic information; required
65 $doc->docid
= $comment['id'];
66 $doc->documenttype
= SEARCH_TYPE_DATA
;
67 $doc->itemtype
= 'comment';
68 $doc->contextid
= $context_id;
70 $doc->title
= get_string('commenton', 'search').' '.$comment['title'];
71 $doc->date
= $comment['modified'];
72 //remove '(ip.ip.ip.ip)' from data record author field
73 $doc->author
= preg_replace('/\(.*?\)/', '', $comment['author']);
74 $doc->contents
= $comment['content'];
75 $doc->url
= data_make_link($data_id, $comment['recordid']);
77 // module specific information; optional
78 $data->database
= $comment['dataid'];
80 // construct the parent class
81 parent
::__construct($doc, $data, $course_id, $comment['groupid'], $comment['userid'], PATH_FOR_SEARCH_TYPE_DATA
);
83 } //ChatCommentSearchDocument
86 * constructs a valid link to a data record content
87 * @param database_id the database reference
88 * @param record_id the record reference
89 * @return a valid url top access the information as a string
91 function data_make_link($database_id, $record_id) {
94 return $CFG->wwwroot
.'/mod/data/view.php?d='.$database_id.'&rid='.$record_id;
98 * fetches all the records for a given database
99 * @param database_id the database
100 * @param typematch a comma separated list of types that should be considered for searching or *
101 * @return an array of objects representing the data records.
103 function data_get_records($database_id, $typematch = '*') {
106 $fieldset = get_records('data_fields', 'dataid', $database_id);
111 {$CFG->prefix}data_content as c,
112 {$CFG->prefix}data_records as r
114 c.recordid = r.id AND
115 r.dataid = {$database_id}
119 $data = get_records_sql($query);
122 foreach($data as $aDatum){
123 if($typematch == '*' ||
preg_match("/\\b{$fieldset[$aDatum->fieldid]->type}\\b/", $typematch)){
124 if (!isset($records[$aDatum->recordid
])){
125 $records[$aDatum->recordid
]['_first'] = $aDatum->content
.' '.$aDatum->content1
.' '.$aDatum->content2
.' '.$aDatum->content3
.' '.$aDatum->content4
.' ';
128 $records[$aDatum->recordid
][$fieldset[$aDatum->fieldid
]->name
] = $aDatum->content
.' '.$aDatum->content1
.' '.$aDatum->content2
.' '.$aDatum->content3
.' '.$aDatum->content4
.' ';
137 * fetches all the comments for a given database
138 * @param database_id the database
139 * @return an array of objects representing the data record comments.
141 function data_get_comments($database_id) {
155 {$CFG->prefix}data_comments as c,
156 {$CFG->prefix}data_records as r
160 $comments = get_records_sql($query);
162 } //data_get_comments
166 * part of search engine API
169 function data_iterator() {
170 $databases = get_records('data');
175 * part of search engine API
176 * @param database the database instance
177 * @return an array of searchable documents
179 function data_get_content_for_index(&$database) {
181 $documents = array();
182 $recordTitles = array();
183 $coursemodule = get_field('modules', 'id', 'name', 'data');
184 $cm = get_record('course_modules', 'course', $database->course
, 'module', $coursemodule, 'instance', $database->id
);
185 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
187 // getting records for indexing
188 $records_content = data_get_records($database->id
, 'text');
189 if ($records_content){
190 foreach(array_keys($records_content) as $aRecordId) {
192 // extract title as first record in order
193 $first = $records_content[$aRecordId]['_first'];
194 unset($records_content[$aRecordId]['_first']);
196 // concatenates all other texts
197 foreach($records_content[$aRecordId] as $aField){
198 $content = @$content.' '.$aField;
200 if (strlen($content) > 0) {
201 unset($recordMetaData);
202 $recordMetaData = get_record('data_records', 'id', $aRecordId);
203 $recordMetaData->title
= $first;
204 $recordTitles[$aRecordId] = $first;
205 $recordMetaData->content
= $content;
206 $documents[] = new DataSearchDocument(get_object_vars($recordMetaData), $database->course
, $context->id
);
211 // getting comments for indexing
212 $records_comments = data_get_comments($database->id
);
213 if ($records_comments){
214 foreach($records_comments as $aComment){
215 $aComment->title
= $recordsTitle[$aComment->recordid
];
216 $documents[] = new DataCommentSearchDocument(get_object_vars($aComment), $database->course
, $context->id
);
220 } //data_get_content_for_index
223 * returns a single data search document based on a data entry id
224 * @param id the id of the record
225 * @param the type of the information
226 * @return a single searchable document
228 function data_single_document($id, $itemtype) {
230 if ($itemtype == 'record'){
232 $recordMetaData = get_record('data_records', 'id', $id);
234 $record_course = get_field('data', 'course', 'id', $recordMetaData->dataid
);
235 $coursemodule = get_field('modules', 'id', 'name', 'data');
236 $cm = get_record('course_modules', 'course', $record_course, 'module', $coursemodule, 'instance', $recordMetaData->dataid
);
237 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
239 $recordData = get_records_select('data_content', "recordid = $id AND type = 'text'", 'recordid');
242 $first = $recordData[0];
243 if (count($recordData) > 1){
244 $others = array_splice($recordData, 0, 1);
245 foreach($others as $aDatum){
246 $accumulator .= $data->content
.' '.$data->content1
.' '.$data->content2
.' '.$data->content3
.' '.$data->content4
.' ';
251 $recordMetaData->title
= $first;
252 $recordMetaData->content
= $accumulator;
254 $documents[] = new DataSearchDocument(get_object_vars($recordMetaData), $record_course, $context->id
);
256 elseif($itemtype == 'comment'){
258 $comment = get_record('data_comments', 'id', $id);
259 $record = get_record('data_records', 'id', $comment->recordid
);
261 $record_course = get_field('data', 'course', 'id', $record->dataid
);
262 $coursemodule = get_field('modules', 'id', 'name', 'data');
263 $cm = get_record('course_modules', 'course', $record_course, 'module', $coursemodule, 'instance', $recordMetaData->dataid
);
264 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
266 $comment->title
= get_field('search_document', 'title', 'docid', $record->id
, 'itemtype', 'record');
267 $comment->dataid
= $record->dataid
;
268 $comment->groupid
= $record->groupid
;
270 $documents[] = new DataCommentSearchDocument(get_object_vars($comment), $record_course, $context->id
);
273 mtrace('Error : bad or missing item type');
275 } //data_single_document
278 * dummy delete function that packs id with itemtype.
279 * this was here for a reason, but I can't remember it at the moment.
282 function data_delete($info, $itemtype) {
284 $object->itemtype
= $itemtype;
289 * returns the var names needed to build a sql query for addition/deletions
292 function data_db_names() {
293 //[primary id], [table name], [time created field name], [time modified field name]
295 array('id', 'data_records', 'timecreated', 'timemodified', 'record'),
296 array('id', 'data_comments', 'created', 'modified', 'comment')
301 * this function handles the access policy to contents indexed as searchable documents. If this
302 * function does not exist, the search engine assumes access is allowed.
303 * When this point is reached, we already know that :
304 * - user is legitimate in the surrounding context
305 * - user may be guest and guest access is allowed to the module
306 * - the function may perform local checks within the module information logic
307 * @param path the access path to the module script code
308 * @param itemtype the information subclassing (usefull for complex modules, defaults to 'standard')
309 * @param this_id the item id within the information class denoted by itemtype. In databases, this id
310 * points out an indexed data record page.
311 * @param user the user record denoting the user who searches
312 * @param group_id the current group used by the user when searching
313 * @return true if access is allowed, false elsewhere
315 function data_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
318 // get the database object and all related stuff
319 if ($itemtype == 'record'){
320 $record = get_record('data_records', 'id', $this_id);
322 elseif($itemtype == 'comment'){
323 $comment = get_record('data_comments', 'id', $this_id);
324 $record = get_record('data_records', 'id', $comment->recordid
);
327 // we do not know what type of information is required
330 $data = get_record('data', 'id', $record->dataid
);
331 $course = get_record('course', 'id', $data->course
);
332 $module_context = get_record('context', 'id', $context_id);
333 $cm = get_record('course_modules', 'id', $module_context->instance
);
334 if (!$cm->visible
and !has_capability('moodle/course:viewhiddenactivities', $module_context)) return false;
336 //group consistency check : checks the following situations about groups
337 // trap if user is not same group and groups are separated
338 $current_group = get_current_group($course->id
);
339 if ((groupmode($course) == SEPARATEGROUPS
) && !ismember($group_id) && !has_capability('moodle/site:accessallgroups', $module_context)) return false;
341 //ownership check : checks the following situations about user
342 // trap if user is not owner and has cannot see other's entries
343 if ($itemtype == 'record'){
344 if ($user->id
!= $record->userid
&& !has_capability('mod/data:viewentry', $module_context) && !has_capability('mod/data:manageentries', $module_context)) return false;
348 // trap if unapproved and has not approval capabilities
349 // TODO : report a potential capability lack of : mod/data:approve
350 $approval = get_field('data_records', 'approved', 'id', $record->id
);
351 if (!$approval && !isteacher($data->course
) && !has_capability('mod/data:manageentries', $module_context)) return false;
353 //minimum records to view check
354 // trap if too few records
355 // TODO : report a potential capability lack of : mod/data:viewhiddenentries
356 $recordsAmount = count_records('data_records', 'dataid', $data->id
);
357 if ($data->requiredentriestoview
> $recordsAmount && !isteacher($data->course
) && !has_capability('mod/data:manageentries', $module_context)) return false;
359 //opening periods check
360 // trap if user has not capability to see hidden records and date is out of opening range
361 // TODO : report a potential capability lack of : mod/data:viewhiddenentries
362 $now = usertime(time());
363 if ($data->timeviewfrom
> 0)
364 if ($now < $data->timeviewfrom
&& !isteacher($data->course
) && !has_capability('mod/data:manageentries', $module_context)) return false;
365 if ($data->timeviewto
> 0)
366 if ($now > $data->timeviewto
&& !isteacher($data->course
) && !has_capability('mod/data:manageentries', $module_context)) return false;
369 } // data_check_text_access