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
13 * special (EXTRA) document handling for user related data
18 * includes and requires
20 require_once($CFG->dirroot
.'/search/documents/document.php');
21 require_once($CFG->dirroot
.'/blog/lib.php');
24 * a class for representing searchable information in user metadata
27 class UserSearchDocument
extends SearchDocument
{
33 public function __construct(&$userhash, $user_id, $context_id) {
36 // generic information; required
37 $doc->docid
= $userhash['id'];
38 $doc->documenttype
= SEARCH_TYPE_USER
;
39 $doc->itemtype
= 'user';
40 $doc->contextid
= $context_id;
42 $user = $DB->get_record('user', array('id' => $user_id));
43 $doc->title
= get_string('user').': '.fullname($user);
44 $doc->date
= ($userhash['lastaccess']) ?
$userhash['lastaccess'] : time() ;
46 //remove '(ip.ip.ip.ip)' from chat author list
47 $doc->author
= $user->id
;
48 $doc->contents
= $userhash['description'];
49 $doc->url
= user_make_link($user_id, 'user');
51 // module specific information; optional
53 // construct the parent class
54 parent
::__construct($doc, $data, 0, 0, $user_id, PATH_FOR_SEARCH_TYPE_USER
);
59 * a class for representing searchable information in user metadata
62 class UserPostSearchDocument
extends SearchDocument
{
68 public function __construct(&$post, $user_id, $context_id) {
71 // generic information; required
72 $doc->docid
= $post['id'];
73 $doc->documenttype
= SEARCH_TYPE_USER
;
74 $doc->itemtype
= 'post';
75 $doc->contextid
= $context_id;
77 $user = $DB->get_record('user', array('id' => $user_id));
79 // we cannot call userdate with relevant locale at indexing time.
80 //$doc->title = get_string('post').': '.fullname($user);
81 $doc->title
= $post['subject'];
82 $doc->date
= $post['created'];
84 //remove '(ip.ip.ip.ip)' from chat author list
85 $doc->author
= fullname($user);
86 $doc->contents
= $post['description'];
87 // $doc->url = user_make_link($user_id, 'post');
88 $doc->url
= user_make_link($post['id'], 'post');
90 // module specific information; optional
92 // construct the parent class
93 parent
::__construct($doc, $data, 0, 0, $user_id, PATH_FOR_SEARCH_TYPE_USER
);
98 * a class for representing searchable information in user metadata
101 class UserBlogAttachmentSearchDocument
extends SearchDocument
{
107 public function __construct(&$post, $context_id) {
110 // generic information; required
111 $doc->docid
= $post['id'];
112 $doc->documenttype
= SEARCH_TYPE_USER
;
113 $doc->itemtype
= 'attachment';
114 $doc->contextid
= $context_id;
116 $user = $DB->get_record('user', 'id', $post['userid']);
118 // we cannot call userdate with relevant locale at indexing time.
119 $doc->title
= get_string('file').' : '.$post['subject'];
120 $doc->date
= $post['created'];
122 //remove '(ip.ip.ip.ip)' from chat author list
123 $doc->author
= fullname($user);
124 $doc->contents
= $post['alltext'];
125 $doc->url
= user_make_link($post['id'], 'attachment');
127 // module specific information; optional
129 // construct the parent class
130 parent
::__construct($doc, $data, 0, 0, $post['userid'], PATH_FOR_SEARCH_TYPE_USER
);
136 * constructs a valid link to a user record
137 * @param int $userid the user
138 * @param string $itemtype
140 * @return a well formed link to user information
142 function user_make_link($itemid, $itemtype) {
145 if ($itemtype == 'user'){
146 return $CFG->wwwroot
.'/user/view.php?id='.$itemid;
147 } elseif ($itemtype == 'post') {
148 return $CFG->wwwroot
.'/blog/index.php?postid='.$itemid;
149 } elseif ($itemtype == 'attachment') {
150 $post = $DB->get_record('post', array('id' => $itemid));
151 if (!$CFG->slasharguments
){
152 return $CFG->wwwroot
."/file.php?file=/blog/attachments/{$post->id}/{$post->attachment}";
154 return $CFG->wwwroot
."/file.php/blog/attachments/{$post->id}/{$post->attachment}";
162 * part of search engine API
166 function user_iterator() {
169 $users = $DB->get_records('user');
174 * part of search engine API
176 * @param reference $user a user record
177 * @return an array of documents generated from data
179 function user_get_content_for_index(&$user) {
182 $documents = array();
184 $userhash = get_object_vars($user);
185 $documents[] = new UserSearchDocument($userhash, $user->id
, null);
187 if ($posts = $DB->get_records('post', array('userid' => $user->id
), 'created')){
188 foreach($posts as $post){
190 $texts[] = $post->subject
;
191 $texts[] = $post->summary
;
192 $texts[] = $post->content
;
193 $post->description
= implode(' ', $texts);
195 // record the attachment if any and physical files can be indexed
196 if (@$CFG->block_search_enable_file_indexing
){
197 if ($post->attachment
){
198 user_get_physical_file($post, null, false, $documents);
202 $posthash = get_object_vars($post);
203 $documents[] = new UserPostSearchDocument($posthash, $user->id
, null);
210 * get text from a physical file
212 * @param object $post a post to whech the file is attached to
213 * @param boolean $context_id if in future we need recording a context along with the search document, pass it here
214 * @param boolean $getsingle if true, returns a single search document, elsewhere return the array
215 * given as documents increased by one
216 * @param array $documents the array of documents, by ref, where to add the new document.
217 * @return a search document when unique or false.
219 function user_get_physical_file(&$post, $context_id, $getsingle, &$documents = null){
222 // cannot index empty references
223 if (empty($post->attachment
)){
224 mtrace("Cannot index, empty reference.");
228 $fileparts = pathinfo($post->attachment
);
229 // cannot index unknown or masked types
230 if (empty($fileparts['extension'])) {
231 mtrace("Cannot index without explicit extension.");
235 // cannot index non existent file
236 $file = "{$CFG->dataroot}/blog/attachments/{$post->id}/{$post->attachment}";
237 if (!file_exists($file)){
238 mtrace("Missing attachment file $file : will not be indexed.");
242 $ext = strtolower($fileparts['extension']);
244 // cannot index unallowed or unhandled types
245 if (!preg_match("/\b$ext\b/i", $CFG->block_search_filetypes
)) {
246 mtrace($fileparts['extension'] . ' is not an allowed extension for indexing');
249 if (file_exists($CFG->dirroot
.'/search/documents/physical_'.$ext.'.php')){
250 include_once($CFG->dirroot
.'/search/documents/physical_'.$ext.'.php');
251 $function_name = 'get_text_for_indexing_'.$ext;
252 $directfile = "blog/attachments/{$post->id}/{$post->attachment}";
253 $post->alltext
= $function_name($post, $directfile);
254 if (!empty($post->alltext
)){
256 $posthash = get_object_vars($post);
257 $single = new UserBlogAttachmentSearchDocument($posthash, $context_id);
258 mtrace("finished attachment {$post->attachment} in {$post->title}");
261 $posthash = get_object_vars($post);
262 $documents[] = new UserBlogAttachmentSearchDocument($posthash, $context_id);
264 mtrace("finished attachment {$post->attachment} in {$post->subject}");
267 mtrace("fulltext handler not found for $ext type");
273 * returns a single user search document
275 * @param composite $id a unique document id made with
276 * @param itemtype the type of information (session is the only type)
278 function user_single_document($id, $itemtype) {
281 if ($itemtype == 'user'){
282 if ($user = $DB->get_record('user', array('id' => $id))){
283 $userhash = get_object_vars($user);
284 return new UserSearchDocument($userhash, $user->id
, 'user', null);
286 } elseif ($itemtype == 'post') {
287 if ($post = $DB->get_record('post', array('id' => $id))){
289 $texts[] = $post->subject
;
290 $texts[] = $post->summary
;
291 $texts[] = $post->content
;
292 $post->description
= implode(" ", $texts);
293 $posthash = get_object_vars($post);
294 return new UserPostSearchDocument($posthash, $post->userid
, 'post', null);
296 } elseif ($itemtype == 'attachment' && @$CFG->block_search_enable_file_indexing
) {
297 if ($post = $DB->get_records('post', array('id' => $id))){
298 if ($post->attachment
){
299 return user_get_physical_file($post, null, true);
307 * dummy delete function that packs id with itemtype.
308 * this was here for a reason, but I can't remember it at the moment.
311 function user_delete($info, $itemtype) {
313 $object->itemtype
= $itemtype;
318 * returns the var names needed to build a sql query for addition/deletions
319 * attachments are indirect records, linked to its post
321 function user_db_names() {
322 //[primary id], [table name], [time created field name], [time modified field name] [itemtype] [select restriction clause]
324 array('id', 'user', 'firstaccess', 'timemodified', 'user'),
325 array('id', 'post', 'created', 'lastmodified', 'post'),
326 array('id', 'post', 'created', 'lastmodified', 'attachment')
331 * this function handles the access policy to contents indexed as searchable documents. If this
332 * function does not exist, the search engine assumes access is allowed.
333 * When this point is reached, we already know that :
334 * - user is legitimate in the surrounding context
335 * - user may be guest and guest access is allowed to the module
336 * - the function may perform local checks within the module information logic
337 * @param string $path the access path to the module script code
338 * @param string $itemtype the information subclassing (usefull for complex modules, defaults to 'standard')
339 * @param int $this_id the item id within the information class denoted by entry_type. In chats, this id
340 * points out a session history which is a close sequence of messages.
341 * @param object $user the user record denoting the user who searches
342 * @param int $group_id the current group used by the user when searching
344 * @return true if access is allowed, false elsewhere
346 function user_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
349 include_once("{$CFG->dirroot}/{$path}/lib.php");
351 if ($itemtype == 'user'){
353 $userrecord = $DB->get_record('user', array('id' => $this_id));
355 // we cannot see nothing from unconfirmed users
356 if (!$userrecord->confirmed
and !has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM
))){
357 if (!empty($CFG->search_access_debug
)) echo "search reject : unconfirmed user ";
360 } elseif ($itemtype == 'post' ||
$itemtype == 'attachment'){
362 $post = $DB->get_record('post', array('id' => $this_id));
363 $userrecord = $DB->get_record('user', array('id' => $post->userid
));
365 // we can try using blog visibility check
366 return blog_user_can_view_user_post($user->id
, $post);
368 $context = $DB->get_record('context', array('id' => $context_id));
374 * this call back is called when displaying the link for some last post processing
377 function user_link_post_processing($title){
380 if ($CFG->block_search_utf8dir
){
381 return mb_convert_encoding($title, 'UTF-8', 'auto');
383 return mb_convert_encoding($title, 'auto', 'UTF-8');