Merge commit 'catalyst/MOODLE_19_STABLE' into mdl19-linuxchix
[moodle-linuxchix.git] / search / documents / data_document.php
blob4b301ffaa82c18b49666a67cec139b7d442bc564
1 <?php
2 /**
3 * Global Search Engine for Moodle
5 * @package search
6 * @category core
7 * @subpackage document_wrappers
8 * @author Valery Fremaux [valery.fremaux@club-internet.fr] > 1.8
9 * @date 2008/03/31
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
20 /**
21 * includes and requires
23 require_once("$CFG->dirroot/search/documents/document.php");
24 require_once("$CFG->dirroot/mod/data/lib.php");
26 /**
27 * a class for representing searchable information (data records)
30 class DataSearchDocument extends SearchDocument {
32 /**
33 * constructor
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);
61 /**
62 * a class for representing searchable information (comments on data records)
65 class DataCommentSearchDocument extends SearchDocument {
67 /**
68 * constructor
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);
92 /**
93 * constructs a valid link to a data record content
94 * @param database_id the database reference
95 * @param record_id the record reference
96 * @uses CFG
97 * @return a valid url top access the information as a string
99 function data_make_link($database_id, $record_id) {
100 global $CFG;
102 return $CFG->wwwroot.'/mod/data/view.php?d='.$database_id.'&amp;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 *
109 * @uses CFG
110 * @return an array of objects representing the data records.
112 function data_get_records($database_id, $typematch = '*') {
113 global $CFG;
115 $fieldset = get_records('data_fields', 'dataid', $database_id);
116 $query = "
117 SELECT
119 FROM
120 {$CFG->prefix}data_content as c,
121 {$CFG->prefix}data_records as r
122 WHERE
123 c.recordid = r.id AND
124 r.dataid = {$database_id}
125 ORDER BY
126 c.fieldid
128 $data = get_records_sql($query);
129 $records = array();
130 if ($data){
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.' ';
135 } else {
136 $records[$aDatum->recordid][$fieldset[$aDatum->fieldid]->name] = $aDatum->content.' '.$aDatum->content1.' '.$aDatum->content2.' '.$aDatum->content3.' '.$aDatum->content4.' ';
141 return $records;
145 * fetches all the comments for a given database
146 * @param database_id the database
147 * @uses CFG
148 * @return an array of objects representing the data record comments.
150 function data_get_comments($database_id) {
151 global $CFG;
153 $query = "
154 SELECT
155 c.id,
156 r.groupid,
157 c.userid,
158 c.recordid,
159 c.content,
160 c.created,
161 c.modified,
162 r.dataid
163 FROM
164 {$CFG->prefix}data_comments as c,
165 {$CFG->prefix}data_records as r
166 WHERE
167 c.recordid = r.id
169 $comments = get_records_sql($query);
170 return $comments;
175 * part of search engine API
178 function data_iterator() {
179 $databases = get_records('data');
180 return $databases;
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);
228 return $documents;
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'){
240 // get main record
241 $recordMetaData = get_record('data_records', 'id', $id);
242 // get context
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);
247 // compute text
248 $recordData = get_records_select('data_content', "recordid = $id AND type = 'text'", 'recordid');
249 $accumulator = '';
250 if ($recordData){
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.' ';
259 // add extra fields
260 $recordMetaData->title = $first;
261 $recordMetaData->content = $accumulator;
262 // make document
263 $documents[] = new DataSearchDocument(get_object_vars($recordMetaData), $record_course, $context->id);
264 } elseif($itemtype == 'comment') {
265 // get main records
266 $comment = get_record('data_comments', 'id', $id);
267 $record = get_record('data_records', 'id', $comment->recordid);
268 // get context
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);
273 // add extra fields
274 $comment->title = get_field('search_document', 'title', 'docid', $record->id, 'itemtype', 'record');
275 $comment->dataid = $record->dataid;
276 $comment->groupid = $record->groupid;
277 // make document
278 $documents[] = new DataCommentSearchDocument(get_object_vars($comment), $record_course, $context->id);
279 } else {
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) {
290 $object->id = $info;
291 $object->itemtype = $itemtype;
292 return $object;
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]
301 return array(
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
320 * @uses CFG
321 * @return true if access is allowed, false elsewhere
323 function data_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
324 global $CFG;
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);
334 else{
335 // we do not know what type of information is required
336 return false;
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 ";
346 return false;
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 ";
355 return false;
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 ";
363 return false;
367 //approval check
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 ";
373 return false;
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 ";
382 return false;
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 ";
392 return false;
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 ";
397 return false;
400 return true;
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');