3 * Global Search Engine for Moodle
7 * @subpackage document_wrappers
8 * @author Valery Fremaux [valery.fremaux@club-internet.fr] > 1.8
10 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
12 * document handling for data activity module
13 * This file contains the mapping between a database object and it's indexable counterpart,
15 * Functions for iterating and retrieving the necessary records are now also included
16 * in this file, rather than mod/data/lib.php
21 * includes and requires
23 require_once("$CFG->dirroot/search/documents/document.php");
24 require_once("$CFG->dirroot/mod/data/lib.php");
27 * a class for representing searchable information (data records)
30 class DataSearchDocument
extends SearchDocument
{
35 public function __construct(&$record, $course_id, $context_id) {
36 // generic information; required
37 $doc->docid
= $record['id'];
38 $doc->documenttype
= SEARCH_TYPE_DATA
;
39 $doc->itemtype
= 'record';
40 $doc->contextid
= $context_id;
42 $doc->title
= $record['title'];
43 $doc->date
= $record['timemodified'];
44 //remove '(ip.ip.ip.ip)' from data record author field
45 if ($record['userid']){
46 $user = get_record('user', 'id', $record['userid']);
48 $doc->author
= (isset($user)) ?
$user->firstname
.' '.$user->lastname
: '' ;
49 $doc->contents
= $record['content'];
50 $doc->url
= data_make_link($record['dataid'], $record['id']);
52 // module specific information; optional
53 // $data->params = serialize(@$record['params']); may be useful
54 $data->database
= $record['dataid'];
56 // construct the parent class
57 parent
::__construct($doc, $data, $course_id, $record['groupid'], $record['userid'], PATH_FOR_SEARCH_TYPE_DATA
);
62 * a class for representing searchable information (comments on data records)
65 class DataCommentSearchDocument
extends SearchDocument
{
70 public function __construct(&$comment, $course_id, $context_id) {
71 // generic information; required
72 $doc->docid
= $comment['id'];
73 $doc->documenttype
= SEARCH_TYPE_DATA
;
74 $doc->itemtype
= 'comment';
75 $doc->contextid
= $context_id;
77 $doc->title
= get_string('commenton', 'search').' '.$comment['title'];
78 $doc->date
= $comment['modified'];
79 //remove '(ip.ip.ip.ip)' from data record author field
80 $doc->author
= preg_replace('/\(.*?\)/', '', $comment['author']);
81 $doc->contents
= $comment['content'];
82 $doc->url
= data_make_link($data_id, $comment['recordid']);
84 // module specific information; optional
85 $data->database
= $comment['dataid'];
87 // construct the parent class
88 parent
::__construct($doc, $data, $course_id, $comment['groupid'], $comment['userid'], PATH_FOR_SEARCH_TYPE_DATA
);
93 * constructs a valid link to a data record content
94 * @param database_id the database reference
95 * @param record_id the record reference
97 * @return a valid url top access the information as a string
99 function data_make_link($database_id, $record_id) {
102 return $CFG->wwwroot
.'/mod/data/view.php?d='.$database_id.'&rid='.$record_id;
106 * fetches all the records for a given database
107 * @param database_id the database
108 * @param typematch a comma separated list of types that should be considered for searching or *
110 * @return an array of objects representing the data records.
112 function data_get_records($database_id, $typematch = '*') {
115 $fieldset = get_records('data_fields', 'dataid', $database_id);
120 {$CFG->prefix}data_content as c,
121 {$CFG->prefix}data_records as r
123 c.recordid = r.id AND
124 r.dataid = {$database_id}
128 $data = get_records_sql($query);
131 foreach($data as $aDatum){
132 if($typematch == '*' ||
preg_match("/\\b{$fieldset[$aDatum->fieldid]->type}\\b/", $typematch)){
133 if (!isset($records[$aDatum->recordid
])){
134 $records[$aDatum->recordid
]['_first'] = $aDatum->content
.' '.$aDatum->content1
.' '.$aDatum->content2
.' '.$aDatum->content3
.' '.$aDatum->content4
.' ';
136 $records[$aDatum->recordid
][$fieldset[$aDatum->fieldid
]->name
] = $aDatum->content
.' '.$aDatum->content1
.' '.$aDatum->content2
.' '.$aDatum->content3
.' '.$aDatum->content4
.' ';
145 * fetches all the comments for a given database
146 * @param database_id the database
148 * @return an array of objects representing the data record comments.
150 function data_get_comments($database_id) {
164 {$CFG->prefix}data_comments as c,
165 {$CFG->prefix}data_records as r
169 $comments = get_records_sql($query);
175 * part of search engine API
178 function data_iterator() {
179 $databases = get_records('data');
184 * part of search engine API
185 * @param database the database instance
186 * @return an array of searchable documents
188 function data_get_content_for_index(&$database) {
190 $documents = array();
191 $recordTitles = array();
192 $coursemodule = get_field('modules', 'id', 'name', 'data');
193 $cm = get_record('course_modules', 'course', $database->course
, 'module', $coursemodule, 'instance', $database->id
);
194 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
196 // getting records for indexing
197 $records_content = data_get_records($database->id
, 'text');
198 if ($records_content){
199 foreach(array_keys($records_content) as $aRecordId) {
201 // extract title as first record in order
202 $first = $records_content[$aRecordId]['_first'];
203 unset($records_content[$aRecordId]['_first']);
205 // concatenates all other texts
206 foreach($records_content[$aRecordId] as $aField){
207 $content = @$content.' '.$aField;
209 if (strlen($content) > 0) {
210 unset($recordMetaData);
211 $recordMetaData = get_record('data_records', 'id', $aRecordId);
212 $recordMetaData->title
= $first;
213 $recordTitles[$aRecordId] = $first;
214 $recordMetaData->content
= $content;
215 $documents[] = new DataSearchDocument(get_object_vars($recordMetaData), $database->course
, $context->id
);
220 // getting comments for indexing
221 $records_comments = data_get_comments($database->id
);
222 if ($records_comments){
223 foreach($records_comments as $aComment){
224 $aComment->title
= $recordsTitle[$aComment->recordid
];
225 $documents[] = new DataCommentSearchDocument(get_object_vars($aComment), $database->course
, $context->id
);
232 * returns a single data search document based on a data entry id
233 * @param id the id of the record
234 * @param the type of the information
235 * @return a single searchable document
237 function data_single_document($id, $itemtype) {
239 if ($itemtype == 'record'){
241 $recordMetaData = get_record('data_records', 'id', $id);
243 $record_course = get_field('data', 'course', 'id', $recordMetaData->dataid
);
244 $coursemodule = get_field('modules', 'id', 'name', 'data');
245 $cm = get_record('course_modules', 'course', $record_course, 'module', $coursemodule, 'instance', $recordMetaData->dataid
);
246 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
248 $recordData = get_records_select('data_content', "recordid = $id AND type = 'text'", 'recordid');
251 $first = $recordData[0];
252 if (count($recordData) > 1){
253 $others = array_splice($recordData, 0, 1);
254 foreach($others as $aDatum){
255 $accumulator .= $data->content
.' '.$data->content1
.' '.$data->content2
.' '.$data->content3
.' '.$data->content4
.' ';
260 $recordMetaData->title
= $first;
261 $recordMetaData->content
= $accumulator;
263 $documents[] = new DataSearchDocument(get_object_vars($recordMetaData), $record_course, $context->id
);
264 } elseif($itemtype == 'comment') {
266 $comment = get_record('data_comments', 'id', $id);
267 $record = get_record('data_records', 'id', $comment->recordid
);
269 $record_course = get_field('data', 'course', 'id', $record->dataid
);
270 $coursemodule = get_field('modules', 'id', 'name', 'data');
271 $cm = get_record('course_modules', 'course', $record_course, 'module', $coursemodule, 'instance', $recordMetaData->dataid
);
272 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
274 $comment->title
= get_field('search_document', 'title', 'docid', $record->id
, 'itemtype', 'record');
275 $comment->dataid
= $record->dataid
;
276 $comment->groupid
= $record->groupid
;
278 $documents[] = new DataCommentSearchDocument(get_object_vars($comment), $record_course, $context->id
);
280 mtrace('Error : bad or missing item type');
285 * dummy delete function that packs id with itemtype.
286 * this was here for a reason, but I can't remember it at the moment.
289 function data_delete($info, $itemtype) {
291 $object->itemtype
= $itemtype;
296 * returns the var names needed to build a sql query for addition/deletions
299 function data_db_names() {
300 //[primary id], [table name], [time created field name], [time modified field name]
302 array('id', 'data_records', 'timecreated', 'timemodified', 'record'),
303 array('id', 'data_comments', 'created', 'modified', 'comment')
308 * this function handles the access policy to contents indexed as searchable documents. If this
309 * function does not exist, the search engine assumes access is allowed.
310 * When this point is reached, we already know that :
311 * - user is legitimate in the surrounding context
312 * - user may be guest and guest access is allowed to the module
313 * - the function may perform local checks within the module information logic
314 * @param path the access path to the module script code
315 * @param itemtype the information subclassing (usefull for complex modules, defaults to 'standard')
316 * @param this_id the item id within the information class denoted by itemtype. In databases, this id
317 * points out an indexed data record page.
318 * @param user the user record denoting the user who searches
319 * @param group_id the current group used by the user when searching
321 * @return true if access is allowed, false elsewhere
323 function data_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
326 // get the database object and all related stuff
327 if ($itemtype == 'record'){
328 $record = get_record('data_records', 'id', $this_id);
330 elseif($itemtype == 'comment'){
331 $comment = get_record('data_comments', 'id', $this_id);
332 $record = get_record('data_records', 'id', $comment->recordid
);
335 // we do not know what type of information is required
338 $data = get_record('data', 'id', $record->dataid
);
339 $context = get_record('context', 'id', $context_id);
340 $cm = get_record('course_modules', 'id', $context->instanceid
);
341 // $cm = get_coursemodule_from_instance('data', $data->id, $data->course);
342 // $context = get_context_instance(CONTEXT_MODULE, $cm->id);
344 if (!$cm->visible
and !has_capability('moodle/course:viewhiddenactivities', $context)) {
345 if (!empty($CFG->search_access_debug
)) echo "search reject : hidden database ";
349 //group consistency check : checks the following situations about groups
350 // trap if user is not same group and groups are separated
351 $current_group = get_current_group($course->id
);
352 $course = get_record('course', 'id', $data->course
);
353 if ((groupmode($course, $cm) == SEPARATEGROUPS
) && !ismember($group_id) && !has_capability('moodle/site:accessallgroups', $context)){
354 if (!empty($CFG->search_access_debug
)) echo "search reject : separated group owned resource ";
358 //ownership check : checks the following situations about user
359 // trap if user is not owner and has cannot see other's entries
360 if ($itemtype == 'record'){
361 if ($user->id
!= $record->userid
&& !has_capability('mod/data:viewentry', $context) && !has_capability('mod/data:manageentries', $context)){
362 if (!empty($CFG->search_access_debug
)) echo "search reject : not owned resource ";
368 // trap if unapproved and has not approval capabilities
369 // TODO : report a potential capability lack of : mod/data:approve
370 $approval = get_field('data_records', 'approved', 'id', $record->id
);
371 if (!$approval && !isteacher($data->course
) && !has_capability('mod/data:manageentries', $context)){
372 if (!empty($CFG->search_access_debug
)) echo "search reject : unapproved resource ";
376 //minimum records to view check
377 // trap if too few records
378 // TODO : report a potential capability lack of : mod/data:viewhiddenentries
379 $recordsAmount = count_records('data_records', 'dataid', $data->id
);
380 if ($data->requiredentriestoview
> $recordsAmount && !isteacher($data->course
) && !has_capability('mod/data:manageentries', $context)) {
381 if (!empty($CFG->search_access_debug
)) echo "search reject : not enough records to view ";
385 //opening periods check
386 // trap if user has not capability to see hidden records and date is out of opening range
387 // TODO : report a potential capability lack of : mod/data:viewhiddenentries
388 $now = usertime(time());
389 if ($data->timeviewfrom
> 0)
390 if ($now < $data->timeviewfrom
&& !isteacher($data->course
) && !has_capability('mod/data:manageentries', $context)) {
391 if (!empty($CFG->search_access_debug
)) echo "search reject : still not open activity ";
394 if ($data->timeviewto
> 0)
395 if ($now > $data->timeviewto
&& !isteacher($data->course
) && !has_capability('mod/data:manageentries', $context)) {
396 if (!empty($CFG->search_access_debug
)) echo "search reject : closed activity ";
404 * post processes the url for cleaner output.
405 * @param string $title
407 function data_link_post_processing($title){
408 return mb_convert_encoding($title, 'UTF-8', 'auto');