mirror of
https://github.com/moodle/moodle.git
synced 2025-08-07 01:46:45 +02:00
global search review and extension for physical files
This commit is contained in:
parent
cf0b12ac83
commit
2f338ab5b0
28 changed files with 3903 additions and 1485 deletions
271
search/documents/chat_document.php
Normal file
271
search/documents/chat_document.php
Normal file
|
@ -0,0 +1,271 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* add-on 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* document handling for chat activity module
|
||||
* This file contains the mapping between a chat history and it's indexable counterpart,
|
||||
*
|
||||
* Functions for iterating and retrieving the necessary records are now also included
|
||||
* in this file, rather than mod/chat/lib.php
|
||||
**/
|
||||
|
||||
require_once("$CFG->dirroot/search/documents/document.php");
|
||||
require_once("$CFG->dirroot/mod/chat/lib.php");
|
||||
|
||||
/*
|
||||
* a class for representing searchable information
|
||||
*
|
||||
**/
|
||||
class ChatTrackSearchDocument extends SearchDocument {
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(&$chatsession, $chat_module_id, $course_id, $group_id, $context_id) {
|
||||
// generic information; required
|
||||
$doc->docid = $chat_module_id.'-'.$chatsession['sessionstart'].'-'.$chatsession['sessionend'];
|
||||
$doc->documenttype = SEARCH_TYPE_CHAT;
|
||||
$doc->itemtype = 'session';
|
||||
$doc->contextid = $context_id;
|
||||
|
||||
$duration = $chatsession['sessionend'] - $chatsession['sessionstart'];
|
||||
// we cannot call userdate with relevant locale at indexing time.
|
||||
$doc->title = get_string('chatreport', 'chat').' '.get_string('openedon', 'search').' TT_'.$chatsession['sessionstart'].'_TT ('.get_string('duration', 'search').' : '.get_string('numseconds', '', $duration).')';
|
||||
$doc->date = $chatsession['sessionend'];
|
||||
|
||||
//remove '(ip.ip.ip.ip)' from chat author list
|
||||
$doc->author = preg_replace('/\(.*?\)/', '', $chatsession['authors']);
|
||||
$doc->contents = $chatsession['content'];
|
||||
$doc->url = chat_make_link($chat_module_id, $chatsession['sessionstart'], $chatsession['sessionend']);
|
||||
|
||||
// module specific information; optional
|
||||
$data->chat = $chat_module_id;
|
||||
|
||||
// construct the parent class
|
||||
parent::__construct($doc, $data, $course_id, $group_id, 0, PATH_FOR_SEARCH_TYPE_CHAT);
|
||||
} //constructor
|
||||
} //ChatTrackSearchDocument
|
||||
|
||||
|
||||
/**
|
||||
* constructs a valid link to a chat content
|
||||
* @param cm_id the chat course module
|
||||
* @param start the start time of the session
|
||||
* @param end th end time of the session
|
||||
* @return a well formed link to session display
|
||||
*/
|
||||
function chat_make_link($cm_id, $start, $end) {
|
||||
global $CFG;
|
||||
|
||||
return $CFG->wwwroot.'/mod/chat/report.php?id='.$cm_id.'&start='.$start.'&end='.$end;
|
||||
} //chat_make_link
|
||||
|
||||
/**
|
||||
* fetches all the records for a given session and assemble them as a unique track
|
||||
* we revamped here the code of report.php for making sessions, but without any output.
|
||||
* note that we should collect sessions "by groups" if groupmode() is SEPARATEGROUPS.
|
||||
* @param chat_id the database
|
||||
* @return an array of objects representing the chat sessions.
|
||||
*/
|
||||
function chat_get_session_tracks($chat_id, $fromtime = 0, $totime = 0) {
|
||||
global $CFG;
|
||||
|
||||
$chat = get_record('chat', 'id', $chat_id);
|
||||
$course = get_record('course', 'id', $chat->course);
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'data');
|
||||
$cm = get_record('course_modules', 'course', $course->id, 'module', $coursemodule, 'instance', $chat->id);
|
||||
$groupmode = groupmode($course, $cm);
|
||||
|
||||
$fromtimeclause = ($fromtime) ? "AND timestamp >= {$fromtime}" : '';
|
||||
$totimeclause = ($totime) ? "AND timestamp <= {$totime}" : '';
|
||||
$tracks = array();
|
||||
$messages = get_records_select('chat_messages', "chatid = '{$chat_id}' $fromtimeclause $totimeclause", "timestamp DESC");
|
||||
if ($messages){
|
||||
// splits discussions against groups
|
||||
$groupedMessages = array();
|
||||
if ($groupmode != SEPARATEGROUPS){
|
||||
foreach($messages as $aMessage){
|
||||
$groupedMessages[$aMessage->groupid][] = $aMessage;
|
||||
}
|
||||
}
|
||||
else{
|
||||
$groupedMessages[-1] = &$messages;
|
||||
}
|
||||
$sessiongap = 5 * 60; // 5 minutes silence means a new session
|
||||
$sessionend = 0;
|
||||
$sessionstart = 0;
|
||||
$sessionusers = array();
|
||||
$lasttime = time();
|
||||
|
||||
foreach ($groupedMessages as $groupId => $messages) { // We are walking BACKWARDS through the messages
|
||||
$messagesleft = count($messages);
|
||||
foreach ($messages as $message) { // We are walking BACKWARDS through the messages
|
||||
$messagesleft --; // Countdown
|
||||
|
||||
if ($message->system) {
|
||||
continue;
|
||||
}
|
||||
// we are within a session track
|
||||
if ((($lasttime - $message->timestamp) < $sessiongap) and $messagesleft) { // Same session
|
||||
if (count($tracks) > 0){
|
||||
if ($message->userid) { // Remember user and count messages
|
||||
$tracks[count($tracks) - 1]->sessionusers[$message->userid] = $message->userid;
|
||||
// update last track (if exists) record appending content (remember : we go backwards)
|
||||
}
|
||||
$tracks[count($tracks) - 1]->content .= ' '.$message->message;
|
||||
$tracks[count($tracks) - 1]->sessionstart = $message->timestamp;
|
||||
}
|
||||
}
|
||||
// we initiate a new session track (backwards)
|
||||
else {
|
||||
$track = new Object();
|
||||
$track->sessionend = $message->timestamp;
|
||||
$track->sessionstart = $message->timestamp;
|
||||
$track->content = $message->message;
|
||||
// reset the accumulator of users
|
||||
$track->sessionusers = array();
|
||||
$track->sessionusers[$message->userid] = $message->userid;
|
||||
$track->groupid = $groupId;
|
||||
$tracks[] = $track;
|
||||
}
|
||||
$lasttime = $message->timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tracks;
|
||||
} //chat_get_session_tracks
|
||||
|
||||
/**
|
||||
* part of search engine API
|
||||
*
|
||||
*/
|
||||
function chat_iterator() {
|
||||
$chatrooms = get_records('chat');
|
||||
return $chatrooms;
|
||||
} //chat_iterator
|
||||
|
||||
/**
|
||||
* part of search engine API
|
||||
*
|
||||
*/
|
||||
function chat_get_content_for_index(&$chat) {
|
||||
$documents = array();
|
||||
$course = get_record('course', 'id', $chat->course);
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'chat');
|
||||
$cm = get_record('course_modules', 'course', $course->id, 'module', $coursemodule, 'instance', $chat->id);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
|
||||
// getting records for indexing
|
||||
$sessionTracks = chat_get_session_tracks($chat->id);
|
||||
if ($sessionTracks){
|
||||
foreach($sessionTracks as $aTrackId => $aTrack) {
|
||||
foreach($aTrack->sessionusers as $aUserId){
|
||||
$user = get_record('user', 'id', $aUserId);
|
||||
$aTrack->authors = ($user) ? $user->firstname.' '.$user->lastname : '' ;
|
||||
$documents[] = new ChatTrackSearchDocument(get_object_vars($aTrack), $cm->id, $chat->course, $aTrack->groupid, $context->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $documents;
|
||||
} //chat_get_content_for_index
|
||||
|
||||
/**
|
||||
* returns a single data search document based on a chat_session id
|
||||
* chat session id is a text composite identifier made of :
|
||||
* - the chat id
|
||||
* - the timestamp when the session starts
|
||||
* - the timestamp when the session ends
|
||||
* @param id the multipart chat session id
|
||||
* @param itemtype the type of information (session is the only type)
|
||||
*/
|
||||
function chat_single_document($id, $itemtype) {
|
||||
list($chat_id, $sessionstart, $sessionend) = split('-', $id);
|
||||
$chat = get_record('chat', 'id', $chat_id);
|
||||
$course = get_record('course', 'id', $chat->course);
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'chat');
|
||||
$cm = get_record('course_modules', 'course', $course->id, 'module', $coursemodule, 'instance', $chat->id);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
|
||||
// should be only one
|
||||
$tracks = chat_get_session_tracks($chat->id, $sessionstart, $sessionstart);
|
||||
if ($tracks){
|
||||
$aTrack = $tracks[0];
|
||||
$documents[] = new ChatTrackSearchDocument(get_object_vars($aTrack), $cm->id, $chat->course, $aTrack->groupid, $context->id);
|
||||
}
|
||||
} //chat_single_document
|
||||
|
||||
/**
|
||||
* dummy delete function that packs id with itemtype.
|
||||
* this was here for a reason, but I can't remember it at the moment.
|
||||
*
|
||||
*/
|
||||
function chat_delete($info, $itemtype) {
|
||||
$object->id = $info;
|
||||
$object->itemtype = $itemtype;
|
||||
return $object;
|
||||
} //chat_delete
|
||||
|
||||
/**
|
||||
* returns the var names needed to build a sql query for addition/deletions
|
||||
* // TODO chat indexable records are virtual. Should proceed in a special way
|
||||
*/
|
||||
function chat_db_names() {
|
||||
//[primary id], [table name], [time created field name], [time modified field name]
|
||||
return null;
|
||||
} //chat_db_names
|
||||
|
||||
/**
|
||||
* this function handles the access policy to contents indexed as searchable documents. If this
|
||||
* function does not exist, the search engine assumes access is allowed.
|
||||
* When this point is reached, we already know that :
|
||||
* - user is legitimate in the surrounding context
|
||||
* - user may be guest and guest access is allowed to the module
|
||||
* - the function may perform local checks within the module information logic
|
||||
* @param path the access path to the module script code
|
||||
* @param itemtype the information subclassing (usefull for complex modules, defaults to 'standard')
|
||||
* @param this_id the item id within the information class denoted by entry_type. In chats, this id
|
||||
* points out a session history which is a close sequence of messages.
|
||||
* @param user the user record denoting the user who searches
|
||||
* @param group_id the current group used by the user when searching
|
||||
* @return true if access is allowed, false elsewhere
|
||||
*/
|
||||
function chat_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
|
||||
global $CFG;
|
||||
|
||||
include_once("{$CFG->dirroot}/{$path}/lib.php");
|
||||
|
||||
list($chat_id, $sessionstart, $sessionend) = split('-', $id);
|
||||
|
||||
// get the chat session and all related stuff
|
||||
$chat = get_record('chat', 'id', $chat_id);
|
||||
$course = get_record('course', 'id', $chat->course);
|
||||
$module_context = get_record('context', 'id', $context_id);
|
||||
$cm = get_record('course_modules', 'id', $module_context->instanceid);
|
||||
if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', $module_context)) return false;
|
||||
|
||||
//group consistency check : checks the following situations about groups
|
||||
// trap if user is not same group and groups are separated
|
||||
$current_group = get_current_group($course->id);
|
||||
if ((groupmode($course) == SEPARATEGROUPS) && !ismember($group_id) && !has_capability('moodle/site:accessallgroups', $module_context)) return false;
|
||||
|
||||
//ownership check : checks the following situations about user
|
||||
// trap if user is not owner and has cannot see other's entries
|
||||
// TODO : typically may be stored into indexing cache
|
||||
if (!has_capability('mod/chat:readlog', $module_context)) return false;
|
||||
|
||||
return true;
|
||||
} //chat_check_text_access
|
||||
|
||||
/**
|
||||
* this call back is called when displaying the link for some last post processing
|
||||
*
|
||||
*/
|
||||
function chat_link_post_processing($title){
|
||||
setLocale(LC_TIME, substr(current_language(), 0, 2));
|
||||
$title = preg_replace('/TT_(.*)_TT/e', "userdate(\\1)", $title);
|
||||
return $title;
|
||||
} //chat_link_post_processing
|
||||
?>
|
370
search/documents/data_document.php
Normal file
370
search/documents/data_document.php
Normal file
|
@ -0,0 +1,370 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* add-on 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* document handling for data activity module
|
||||
* This file contains the mapping between a database object and it's indexable counterpart,
|
||||
*
|
||||
* Functions for iterating and retrieving the necessary records are now also included
|
||||
* in this file, rather than mod/data/lib.php
|
||||
**/
|
||||
|
||||
require_once("$CFG->dirroot/search/documents/document.php");
|
||||
require_once("$CFG->dirroot/mod/data/lib.php");
|
||||
|
||||
/*
|
||||
* a class for representing searchable information (data records)
|
||||
*
|
||||
**/
|
||||
class DataSearchDocument extends SearchDocument {
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(&$record, $course_id, $context_id) {
|
||||
// generic information; required
|
||||
$doc->docid = $record['id'];
|
||||
$doc->documenttype = SEARCH_TYPE_DATA;
|
||||
$doc->itemtype = 'record';
|
||||
$doc->contextid = $context_id;
|
||||
|
||||
$doc->title = $record['title'];
|
||||
$doc->date = $record['timemodified'];
|
||||
//remove '(ip.ip.ip.ip)' from data record author field
|
||||
if ($record['userid']){
|
||||
$user = get_record('user', 'id', $record['userid']);
|
||||
}
|
||||
$doc->author = (isset($user)) ? $user->firstname.' '.$user->lastname : '' ;
|
||||
$doc->contents = $record['content'];
|
||||
$doc->url = data_make_link($record['dataid'], $record['id']);
|
||||
|
||||
// module specific information; optional
|
||||
// $data->params = serialize(@$record['params']); may be useful
|
||||
$data->database = $record['dataid'];
|
||||
|
||||
// construct the parent class
|
||||
parent::__construct($doc, $data, $course_id, $record['groupid'], $record['userid'], PATH_FOR_SEARCH_TYPE_DATA);
|
||||
} //constructor
|
||||
} //ChatSearchDocument
|
||||
|
||||
/*
|
||||
* a class for representing searchable information (comments on data records)
|
||||
*
|
||||
**/
|
||||
class DataCommentSearchDocument extends SearchDocument {
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(&$comment, $course_id, $context_id) {
|
||||
// generic information; required
|
||||
$doc->docid = $comment['id'];
|
||||
$doc->documenttype = SEARCH_TYPE_DATA;
|
||||
$doc->itemtype = 'comment';
|
||||
$doc->contextid = $context_id;
|
||||
|
||||
$doc->title = get_string('commenton', 'search').' '.$comment['title'];
|
||||
$doc->date = $comment['modified'];
|
||||
//remove '(ip.ip.ip.ip)' from data record author field
|
||||
$doc->author = preg_replace('/\(.*?\)/', '', $comment['author']);
|
||||
$doc->contents = $comment['content'];
|
||||
$doc->url = data_make_link($data_id, $comment['recordid']);
|
||||
|
||||
// module specific information; optional
|
||||
$data->database = $comment['dataid'];
|
||||
|
||||
// construct the parent class
|
||||
parent::__construct($doc, $data, $course_id, $comment['groupid'], $comment['userid'], PATH_FOR_SEARCH_TYPE_DATA);
|
||||
} //constructor
|
||||
} //ChatCommentSearchDocument
|
||||
|
||||
/**
|
||||
* constructs a valid link to a data record content
|
||||
* @param database_id the database reference
|
||||
* @param record_id the record reference
|
||||
* @return a valid url top access the information as a string
|
||||
*/
|
||||
function data_make_link($database_id, $record_id) {
|
||||
global $CFG;
|
||||
|
||||
return $CFG->wwwroot.'/mod/data/view.php?d='.$database_id.'&rid='.$record_id;
|
||||
} //data_make_link
|
||||
|
||||
/**
|
||||
* fetches all the records for a given database
|
||||
* @param database_id the database
|
||||
* @param typematch a comma separated list of types that should be considered for searching or *
|
||||
* @return an array of objects representing the data records.
|
||||
*/
|
||||
function data_get_records($database_id, $typematch = '*') {
|
||||
global $CFG;
|
||||
|
||||
$fieldset = get_records('data_fields', 'dataid', $database_id);
|
||||
$query = "
|
||||
SELECT
|
||||
c.*
|
||||
FROM
|
||||
{$CFG->prefix}data_content as c,
|
||||
{$CFG->prefix}data_records as r
|
||||
WHERE
|
||||
c.recordid = r.id AND
|
||||
r.dataid = {$database_id}
|
||||
ORDER BY
|
||||
c.fieldid
|
||||
";
|
||||
$data = get_records_sql($query);
|
||||
$records = array();
|
||||
if ($data){
|
||||
foreach($data as $aDatum){
|
||||
if($typematch == '*' || preg_match("/\\b{$fieldset[$aDatum->fieldid]->type}\\b/", $typematch)){
|
||||
if (!isset($records[$aDatum->recordid])){
|
||||
$records[$aDatum->recordid]['_first'] = $aDatum->content.' '.$aDatum->content1.' '.$aDatum->content2.' '.$aDatum->content3.' '.$aDatum->content4.' ';
|
||||
}
|
||||
else{
|
||||
$records[$aDatum->recordid][$fieldset[$aDatum->fieldid]->name] = $aDatum->content.' '.$aDatum->content1.' '.$aDatum->content2.' '.$aDatum->content3.' '.$aDatum->content4.' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $records;
|
||||
} //data_get_records
|
||||
|
||||
/**
|
||||
* fetches all the comments for a given database
|
||||
* @param database_id the database
|
||||
* @return an array of objects representing the data record comments.
|
||||
*/
|
||||
function data_get_comments($database_id) {
|
||||
global $CFG;
|
||||
|
||||
$query = "
|
||||
SELECT
|
||||
c.id,
|
||||
r.groupid,
|
||||
c.userid,
|
||||
c.recordid,
|
||||
c.content,
|
||||
c.created,
|
||||
c.modified,
|
||||
r.dataid
|
||||
FROM
|
||||
{$CFG->prefix}data_comments as c,
|
||||
{$CFG->prefix}data_records as r
|
||||
WHERE
|
||||
c.recordid = r.id
|
||||
";
|
||||
$comments = get_records_sql($query);
|
||||
return $comments;
|
||||
} //data_get_comments
|
||||
|
||||
|
||||
/**
|
||||
* part of search engine API
|
||||
*
|
||||
*/
|
||||
function data_iterator() {
|
||||
$databases = get_records('data');
|
||||
return $databases;
|
||||
} //data_iterator
|
||||
|
||||
/**
|
||||
* part of search engine API
|
||||
* @param database the database instance
|
||||
* @return an array of searchable documents
|
||||
*/
|
||||
function data_get_content_for_index(&$database) {
|
||||
|
||||
$documents = array();
|
||||
$recordTitles = array();
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'data');
|
||||
$cm = get_record('course_modules', 'course', $database->course, 'module', $coursemodule, 'instance', $database->id);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
|
||||
// getting records for indexing
|
||||
$records_content = data_get_records($database->id, 'text');
|
||||
if ($records_content){
|
||||
foreach(array_keys($records_content) as $aRecordId) {
|
||||
|
||||
// extract title as first record in order
|
||||
$first = $records_content[$aRecordId]['_first'];
|
||||
unset($records_content[$aRecordId]['_first']);
|
||||
|
||||
// concatenates all other texts
|
||||
foreach($records_content[$aRecordId] as $aField){
|
||||
$content = @$content.' '.$aField;
|
||||
}
|
||||
if (strlen($content) > 0) {
|
||||
unset($recordMetaData);
|
||||
$recordMetaData = get_record('data_records', 'id', $aRecordId);
|
||||
$recordMetaData->title = $first;
|
||||
$recordTitles[$aRecordId] = $first;
|
||||
$recordMetaData->content = $content;
|
||||
$documents[] = new DataSearchDocument(get_object_vars($recordMetaData), $database->course, $context->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getting comments for indexing
|
||||
$records_comments = data_get_comments($database->id);
|
||||
if ($records_comments){
|
||||
foreach($records_comments as $aComment){
|
||||
$aComment->title = $recordsTitle[$aComment->recordid];
|
||||
$documents[] = new DataCommentSearchDocument(get_object_vars($aComment), $database->course, $context->id);
|
||||
}
|
||||
}
|
||||
return $documents;
|
||||
} //data_get_content_for_index
|
||||
|
||||
/**
|
||||
* returns a single data search document based on a data entry id
|
||||
* @param id the id of the record
|
||||
* @param the type of the information
|
||||
* @return a single searchable document
|
||||
*/
|
||||
function data_single_document($id, $itemtype) {
|
||||
|
||||
if ($itemtype == 'record'){
|
||||
// get main record
|
||||
$recordMetaData = get_record('data_records', 'id', $id);
|
||||
// get context
|
||||
$record_course = get_field('data', 'course', 'id', $recordMetaData->dataid);
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'data');
|
||||
$cm = get_record('course_modules', 'course', $record_course, 'module', $coursemodule, 'instance', $recordMetaData->dataid);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
// compute text
|
||||
$recordData = get_records_select('data_content', "recordid = $id AND type = 'text'", 'recordid');
|
||||
$accumulator = '';
|
||||
if ($recordData){
|
||||
$first = $recordData[0];
|
||||
if (count($recordData) > 1){
|
||||
$others = array_splice($recordData, 0, 1);
|
||||
foreach($others as $aDatum){
|
||||
$accumulator .= $data->content.' '.$data->content1.' '.$data->content2.' '.$data->content3.' '.$data->content4.' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
// add extra fields
|
||||
$recordMetaData->title = $first;
|
||||
$recordMetaData->content = $accumulator;
|
||||
// make document
|
||||
$documents[] = new DataSearchDocument(get_object_vars($recordMetaData), $record_course, $context->id);
|
||||
}
|
||||
elseif($itemtype == 'comment'){
|
||||
// get main records
|
||||
$comment = get_record('data_comments', 'id', $id);
|
||||
$record = get_record('data_records', 'id', $comment->recordid);
|
||||
// get context
|
||||
$record_course = get_field('data', 'course', 'id', $record->dataid);
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'data');
|
||||
$cm = get_record('course_modules', 'course', $record_course, 'module', $coursemodule, 'instance', $recordMetaData->dataid);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
// add extra fields
|
||||
$comment->title = get_field('search_document', 'title', 'docid', $record->id, 'itemtype', 'record');
|
||||
$comment->dataid = $record->dataid;
|
||||
$comment->groupid = $record->groupid;
|
||||
// make document
|
||||
$documents[] = new DataCommentSearchDocument(get_object_vars($comment), $record_course, $context->id);
|
||||
}
|
||||
else{
|
||||
mtrace('Error : bad or missing item type');
|
||||
}
|
||||
} //data_single_document
|
||||
|
||||
/**
|
||||
* dummy delete function that packs id with itemtype.
|
||||
* this was here for a reason, but I can't remember it at the moment.
|
||||
*
|
||||
*/
|
||||
function data_delete($info, $itemtype) {
|
||||
$object->id = $info;
|
||||
$object->itemtype = $itemtype;
|
||||
return $object;
|
||||
} //data_delete
|
||||
|
||||
/**
|
||||
* returns the var names needed to build a sql query for addition/deletions
|
||||
*
|
||||
*/
|
||||
function data_db_names() {
|
||||
//[primary id], [table name], [time created field name], [time modified field name]
|
||||
return array(
|
||||
array('id', 'data_records', 'timecreated', 'timemodified', 'record'),
|
||||
array('id', 'data_comments', 'created', 'modified', 'comment')
|
||||
);
|
||||
} //data_db_names
|
||||
|
||||
/**
|
||||
* this function handles the access policy to contents indexed as searchable documents. If this
|
||||
* function does not exist, the search engine assumes access is allowed.
|
||||
* When this point is reached, we already know that :
|
||||
* - user is legitimate in the surrounding context
|
||||
* - user may be guest and guest access is allowed to the module
|
||||
* - the function may perform local checks within the module information logic
|
||||
* @param path the access path to the module script code
|
||||
* @param itemtype the information subclassing (usefull for complex modules, defaults to 'standard')
|
||||
* @param this_id the item id within the information class denoted by itemtype. In databases, this id
|
||||
* points out an indexed data record page.
|
||||
* @param user the user record denoting the user who searches
|
||||
* @param group_id the current group used by the user when searching
|
||||
* @return true if access is allowed, false elsewhere
|
||||
*/
|
||||
function data_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
|
||||
global $CFG;
|
||||
|
||||
// get the database object and all related stuff
|
||||
if ($itemtype == 'record'){
|
||||
$record = get_record('data_records', 'id', $this_id);
|
||||
}
|
||||
elseif($itemtype == 'comment'){
|
||||
$comment = get_record('data_comments', 'id', $this_id);
|
||||
$record = get_record('data_records', 'id', $comment->recordid);
|
||||
}
|
||||
else{
|
||||
// we do not know what type of information is required
|
||||
return false;
|
||||
}
|
||||
$data = get_record('data', 'id', $record->dataid);
|
||||
$course = get_record('course', 'id', $data->course);
|
||||
$module_context = get_record('context', 'id', $context_id);
|
||||
$cm = get_record('course_modules', 'id', $module_context->instance);
|
||||
if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', $module_context)) return false;
|
||||
|
||||
//group consistency check : checks the following situations about groups
|
||||
// trap if user is not same group and groups are separated
|
||||
$current_group = get_current_group($course->id);
|
||||
if ((groupmode($course) == SEPARATEGROUPS) && !ismember($group_id) && !has_capability('moodle/site:accessallgroups', $module_context)) return false;
|
||||
|
||||
//ownership check : checks the following situations about user
|
||||
// trap if user is not owner and has cannot see other's entries
|
||||
if ($itemtype == 'record'){
|
||||
if ($user->id != $record->userid && !has_capability('mod/data:viewentry', $module_context) && !has_capability('mod/data:manageentries', $module_context)) return false;
|
||||
}
|
||||
|
||||
//approval check
|
||||
// trap if unapproved and has not approval capabilities
|
||||
// TODO : report a potential capability lack of : mod/data:approve
|
||||
$approval = get_field('data_records', 'approved', 'id', $record->id);
|
||||
if (!$approval && !isteacher($data->course) && !has_capability('mod/data:manageentries', $module_context)) return false;
|
||||
|
||||
//minimum records to view check
|
||||
// trap if too few records
|
||||
// TODO : report a potential capability lack of : mod/data:viewhiddenentries
|
||||
$recordsAmount = count_records('data_records', 'dataid', $data->id);
|
||||
if ($data->requiredentriestoview > $recordsAmount && !isteacher($data->course) && !has_capability('mod/data:manageentries', $module_context)) return false;
|
||||
|
||||
//opening periods check
|
||||
// trap if user has not capability to see hidden records and date is out of opening range
|
||||
// TODO : report a potential capability lack of : mod/data:viewhiddenentries
|
||||
$now = usertime(time());
|
||||
if ($data->timeviewfrom > 0)
|
||||
if ($now < $data->timeviewfrom && !isteacher($data->course) && !has_capability('mod/data:manageentries', $module_context)) return false;
|
||||
if ($data->timeviewto > 0)
|
||||
if ($now > $data->timeviewto && !isteacher($data->course) && !has_capability('mod/data:manageentries', $module_context)) return false;
|
||||
|
||||
return true;
|
||||
} // data_check_text_access
|
||||
?>
|
|
@ -1,24 +1,58 @@
|
|||
<?php
|
||||
/* Base search document from which other module/block types can
|
||||
* extend.
|
||||
* */
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* Michael Champanis (mchampan) [cynnical@gmail.com]
|
||||
* review 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* Base search document from which other module/block types can
|
||||
* extend.
|
||||
**/
|
||||
|
||||
abstract class SearchDocument extends Zend_Search_Lucene_Document {
|
||||
public function __construct(&$doc, &$data, $document_type, $course_id, $group_id) {
|
||||
$this->addField(Zend_Search_Lucene_Field::Keyword('docid', $doc->docid));
|
||||
$this->addField(Zend_Search_Lucene_Field::Text('title', $doc->title));
|
||||
$this->addField(Zend_Search_Lucene_Field::Text('author', $doc->author));
|
||||
$this->addField(Zend_Search_Lucene_Field::UnStored('contents', $doc->contents));
|
||||
$this->addField(Zend_Search_Lucene_Field::UnIndexed('url', $doc->url));
|
||||
$this->addField(Zend_Search_Lucene_Field::UnIndexed('date', $doc->date));
|
||||
|
||||
//additional data added on a per-module basis
|
||||
$this->addField(Zend_Search_Lucene_Field::Binary('data', serialize($data)));
|
||||
|
||||
$this->addField(Zend_Search_Lucene_Field::Keyword('doctype', $document_type));
|
||||
$this->addField(Zend_Search_Lucene_Field::Keyword('course_id', $course_id));
|
||||
$this->addField(Zend_Search_Lucene_Field::Keyword('group_id', $group_id));
|
||||
abstract class SearchDocument extends Zend_Search_Lucene_Document {
|
||||
public function __construct(&$doc, &$data, $course_id, $group_id, $user_id, $path) {
|
||||
//document identification and indexing
|
||||
$this->addField(Zend_Search_Lucene_Field::Keyword('docid', $doc->docid));
|
||||
//document type : the name of the Moodle element that manages it
|
||||
$this->addField(Zend_Search_Lucene_Field::Keyword('doctype', $doc->documenttype));
|
||||
//allows subclassing information from complex modules.
|
||||
$this->addField(Zend_Search_Lucene_Field::Keyword('itemtype', $doc->itemtype));
|
||||
//caches the course context.
|
||||
$this->addField(Zend_Search_Lucene_Field::Keyword('course_id', $course_id));
|
||||
//caches the originator's group.
|
||||
$this->addField(Zend_Search_Lucene_Field::Keyword('group_id', $group_id));
|
||||
//caches the originator if any
|
||||
$this->addField(Zend_Search_Lucene_Field::Keyword('user_id', $user_id));
|
||||
// caches the context of this information. i-e, the context in which this information
|
||||
// is being produced/attached. Speeds up the "check for access" process as context in
|
||||
// which the information resides (a course, a module, a block, the site) is stable.
|
||||
$this->addField(Zend_Search_Lucene_Field::UnIndexed('context_id', $doc->contextid));
|
||||
|
||||
//data for document
|
||||
$this->addField(Zend_Search_Lucene_Field::Text('title', $doc->title));
|
||||
$this->addField(Zend_Search_Lucene_Field::Text('author', $doc->author));
|
||||
$this->addField(Zend_Search_Lucene_Field::UnStored('contents', $doc->contents));
|
||||
$this->addField(Zend_Search_Lucene_Field::UnIndexed('url', $doc->url));
|
||||
$this->addField(Zend_Search_Lucene_Field::UnIndexed('date', $doc->date));
|
||||
|
||||
//additional data added on a per-module basis
|
||||
$this->addField(Zend_Search_Lucene_Field::Binary('data', serialize($data)));
|
||||
|
||||
// adding a path allows the document to know where to find specific library calls
|
||||
// for checking access to a module or block content. The Lucene records should only
|
||||
// be responsible to bring back to that call sufficient and consistent information
|
||||
// in order to perform the check.
|
||||
$this->addField(Zend_Search_Lucene_Field::UnIndexed('path', $path));
|
||||
/*
|
||||
// adding a capability set required for viewing. -1 if no capability required.
|
||||
// the capability required for viewing is depending on the local situation
|
||||
// of the document. each module should provide this information when pushing
|
||||
// out search document structure. Although capability model should be kept flat
|
||||
// there is no exclusion some module or block developpers use logical combinations
|
||||
// of multiple capabilities in their code. This possibility should be left open here.
|
||||
$this->addField(Zend_Search_Lucene_Field::UnIndexed('capabilities', $caps));
|
||||
*/
|
||||
} //constructor
|
||||
} //SearchDocument
|
||||
} //SearchDocument
|
||||
|
||||
?>
|
|
@ -1,135 +1,269 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* Michael Champanis (mchampan) [cynnical@gmail.com]
|
||||
* review 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* document handling for forum activity module
|
||||
* This file contains the mapping between a forum post and it's indexable counterpart,
|
||||
*
|
||||
* Functions for iterating and retrieving the necessary records are now also included
|
||||
* in this file, rather than mod/forum/lib.php
|
||||
**/
|
||||
/* see wiki_document.php for descriptions */
|
||||
|
||||
/* see wiki_document.php for descriptions */
|
||||
require_once("$CFG->dirroot/search/documents/document.php");
|
||||
require_once("$CFG->dirroot/mod/forum/lib.php");
|
||||
|
||||
require_once("$CFG->dirroot/search/documents/document.php");
|
||||
require_once("$CFG->dirroot/mod/forum/lib.php");
|
||||
/*
|
||||
* a class for representing searchable information
|
||||
*
|
||||
**/
|
||||
class ForumSearchDocument extends SearchDocument {
|
||||
|
||||
class ForumSearchDocument extends SearchDocument {
|
||||
public function __construct(&$post, $forum_id, $course_id, $group_id) {
|
||||
// generic information
|
||||
$doc->docid = $post['id'];
|
||||
$doc->title = $post['subject'];
|
||||
$doc->author = $post['firstname']." ".$post['lastname'];
|
||||
$doc->contents = $post['message'];
|
||||
$doc->date = $post['created'];
|
||||
/**
|
||||
* constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(&$post, $forum_id, $course_id, $itemtype, $context_id) {
|
||||
// generic information
|
||||
$doc->docid = $post['id'];
|
||||
$doc->documenttype = SEARCH_TYPE_FORUM;
|
||||
$doc->itemtype = $itemtype;
|
||||
$doc->contextid = $context_id;
|
||||
|
||||
$doc->url = forum_make_link($post['discussion'], $post['id']);
|
||||
|
||||
// module specific information
|
||||
$data->forum = $forum_id;
|
||||
$data->discussion = $post['discussion'];
|
||||
|
||||
parent::__construct($doc, $data, SEARCH_TYPE_FORUM, $course_id, $group_id);
|
||||
$doc->title = $post['subject'];
|
||||
$doc->author = $post['firstname']." ".$post['lastname'];
|
||||
$doc->contents = $post['message'];
|
||||
$doc->date = $post['created'];
|
||||
$doc->url = forum_make_link($post['discussion'], $post['id']);
|
||||
|
||||
// module specific information
|
||||
$data->forum = $forum_id;
|
||||
$data->discussion = $post['discussion'];
|
||||
|
||||
parent::__construct($doc, $data, $course_id, $post['groupid'], $post['userid'], PATH_FOR_SEARCH_TYPE_FORUM);
|
||||
} //constructor
|
||||
} //ForumSearchDocument
|
||||
} //ForumSearchDocument
|
||||
|
||||
function forum_make_link($discussion_id, $post_id) {
|
||||
/**
|
||||
* constructs a valid link to a chat content
|
||||
* @param discussion_id the discussion
|
||||
* @param post_id the id of a single post
|
||||
* @return a well formed link to forum message display
|
||||
*/
|
||||
function forum_make_link($discussion_id, $post_id) {
|
||||
global $CFG;
|
||||
|
||||
return $CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion_id.'#'.$post_id;
|
||||
} //forum_make_link
|
||||
} //forum_make_link
|
||||
|
||||
function forum_iterator() {
|
||||
//no @ = Undefined index: 82 in moodle/lib/datalib.php on line 2671
|
||||
return @get_all_instances_in_courses("forum", get_courses());
|
||||
} //forum_iterator
|
||||
/**
|
||||
* search standard API
|
||||
*
|
||||
*/
|
||||
function forum_iterator() {
|
||||
$forums = get_records('forum');
|
||||
return $forums;
|
||||
} //forum_iterator
|
||||
|
||||
function forum_get_content_for_index(&$forum) {
|
||||
$documents = array();
|
||||
if (!$forum) return $documents;
|
||||
/**
|
||||
* search standard API
|
||||
* @param forum a forum instance
|
||||
* @return an array of searchable documents
|
||||
*/
|
||||
function forum_get_content_for_index(&$forum) {
|
||||
|
||||
$posts = forum_get_discussions_fast($forum->id);
|
||||
if (!$posts) return $documents;
|
||||
$documents = array();
|
||||
if (!$forum) return $documents;
|
||||
|
||||
while (!$posts->EOF) {
|
||||
$post = $posts->fields;
|
||||
$posts = forum_get_discussions_fast($forum->id);
|
||||
if (!$posts) return $documents;
|
||||
|
||||
if (is_array($post)) {
|
||||
if (strlen($post['message']) > 0 && ($post['deleted'] != 1)) {
|
||||
$documents[] = new ForumSearchDocument($post, $forum->id, $forum->course, $post['groupid']);
|
||||
} //if
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'forum');
|
||||
$cm = get_record('course_modules', 'course', $forum->course, 'module', $coursemodule, 'instance', $forum->id);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
|
||||
if ($children = forum_get_child_posts_fast($post['id'], $forum->id)) {
|
||||
while (!$children->EOF) {
|
||||
$child = $children->fields;
|
||||
foreach($posts as $aPost) {
|
||||
$aPost->itemtype = 'head';
|
||||
if ($aPost) {
|
||||
if (strlen($aPost->message) > 0) {
|
||||
$documents[] = new ForumSearchDocument(get_object_vars($aPost), $forum->id, $forum->course, 'head', $context->id);
|
||||
}
|
||||
if ($children = forum_get_child_posts_fast($aPost->id, $forum->id)) {
|
||||
foreach($children as $aChild) {
|
||||
$aChild->itemtype = 'post';
|
||||
if (strlen($aChild->message) > 0) {
|
||||
$documents[] = new ForumSearchDocument(get_object_vars($child), $forum->id, $forum->course, 'post', $context->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $documents;
|
||||
} //forum_get_content_for_index
|
||||
|
||||
if (strlen($child['message']) > 0 && ($child['deleted'] != 1)) {
|
||||
$documents[] = new ForumSearchDocument($child, $forum->id, $forum->course, $post['groupid']);
|
||||
} //if
|
||||
/**
|
||||
* returns a single forum search document based on a forum entry id
|
||||
* @param id an id for a single information stub
|
||||
* @param itemtype the type of information
|
||||
*/
|
||||
function forum_single_document($id, $itemtype) {
|
||||
|
||||
$children->MoveNext();
|
||||
} //foreach
|
||||
} //if
|
||||
} //if
|
||||
// both known item types are posts so get them the same way
|
||||
$post = get_record('forum_posts', 'id', $id);
|
||||
$discussion = get_record('forum_discussions', 'id', $post->discussion);
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'forum');
|
||||
$cm = get_record('course_modules', 'course', $discussion->course, 'module', $coursemodule, 'instance', $discussion->forum);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
return new ForumSearchDocument(get_object_vars($post), $discussion->forum, $discussion->course, $itemtype, $context->id);
|
||||
} //forum_single_document
|
||||
|
||||
$posts->MoveNext();
|
||||
} //foreach
|
||||
/**
|
||||
* dummy delete function that aggregates id with itemtype.
|
||||
* this was here for a reason, but I can't remember it at the moment.
|
||||
*
|
||||
*/
|
||||
function forum_delete($info, $itemtype) {
|
||||
$object->id = $info;
|
||||
$object->itemtype = $itemtype;
|
||||
return $object;
|
||||
} //forum_delete
|
||||
|
||||
return $documents;
|
||||
} //forum_get_content_for_index
|
||||
|
||||
//returns a single forum search document based on a forum_entry id
|
||||
function forum_single_document($id) {
|
||||
$posts = get_recordset('forum_posts', 'id', $id);
|
||||
$post = $posts->fields;
|
||||
|
||||
$discussions = get_recordset('forum_discussions', 'id', $post['discussion']);
|
||||
$discussion = $discussions->fields;
|
||||
|
||||
$forums = get_recordset('forum', 'id', $discussion['forum']);
|
||||
$forum = $forums->fields;
|
||||
|
||||
return new ForumSearchDocument($post, $forum['id'], $forum['course'], $post['groupid']);
|
||||
} //forum_single_document
|
||||
|
||||
function forum_delete($info) {
|
||||
return $info;
|
||||
} //forum_delete
|
||||
|
||||
//returns the var names needed to build a sql query for addition/deletions
|
||||
function forum_db_names() {
|
||||
/**
|
||||
* returns the var names needed to build a sql query for addition/deletions
|
||||
*
|
||||
*/
|
||||
function forum_db_names() {
|
||||
//[primary id], [table name], [time created field name], [time modified field name]
|
||||
return array('id', 'forum_posts', 'created', 'modified');
|
||||
} //forum_db_names
|
||||
return array(
|
||||
array('id', 'forum_posts', 'created', 'modified', 'head', 'parent = 0'),
|
||||
array('id', 'forum_posts', 'created', 'modified', 'post', 'parent != 0')
|
||||
);
|
||||
} //forum_db_names
|
||||
|
||||
//reworked faster version from /mod/forum/lib.php
|
||||
function forum_get_discussions_fast($forum) {
|
||||
/**
|
||||
* reworked faster version from /mod/forum/lib.php
|
||||
* @param forum_id a forum identifier
|
||||
* @return an array of posts
|
||||
*/
|
||||
function forum_get_discussions_fast($forum_id) {
|
||||
global $CFG, $USER;
|
||||
|
||||
|
||||
$timelimit='';
|
||||
|
||||
if (!empty($CFG->forum_enabletimedposts)) {
|
||||
if (!((isadmin() and !empty($CFG->admineditalways)) || isteacher(get_field('forum', 'course', 'id', $forum)))) {
|
||||
$now = time();
|
||||
$timelimit = " AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')";
|
||||
if (!empty($USER->id)) {
|
||||
$timelimit .= " OR d.userid = '$USER->id'";
|
||||
if (!((isadmin() and !empty($CFG->admineditalways)) || isteacher(get_field('forum', 'course', 'id', $forum_id)))) {
|
||||
$now = time();
|
||||
$timelimit = " AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')";
|
||||
if (!empty($USER->id)) {
|
||||
$timelimit .= " OR d.userid = '$USER->id'";
|
||||
}
|
||||
$timelimit .= ')';
|
||||
}
|
||||
$timelimit .= ')';
|
||||
}
|
||||
}
|
||||
|
||||
$query = "
|
||||
SELECT
|
||||
p.id,
|
||||
p.subject,
|
||||
p.discussion,
|
||||
p.message,
|
||||
p.created,
|
||||
d.groupid,
|
||||
p.userid,
|
||||
u.firstname,
|
||||
u.lastname
|
||||
FROM
|
||||
{$CFG->prefix}forum_discussions d
|
||||
JOIN
|
||||
{$CFG->prefix}forum_posts p
|
||||
ON
|
||||
p.discussion = d.id
|
||||
JOIN
|
||||
{$CFG->prefix}user u
|
||||
ON
|
||||
p.userid = u.id
|
||||
WHERE
|
||||
d.forum = '{$forum_id}' AND
|
||||
p.parent = 0
|
||||
$timelimit
|
||||
ORDER BY
|
||||
d.timemodified DESC
|
||||
";
|
||||
return get_records_sql($query);
|
||||
} //forum_get_discussions_fast
|
||||
|
||||
return get_recordset_sql("SELECT p.id, p.subject, p.discussion, p.message,
|
||||
p.deleted, d.groupid, u.firstname, u.lastname
|
||||
FROM {$CFG->prefix}forum_discussions d
|
||||
JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
|
||||
JOIN {$CFG->prefix}user u ON p.userid = u.id
|
||||
WHERE d.forum = '$forum'
|
||||
AND p.parent = 0
|
||||
$timelimit
|
||||
ORDER BY d.timemodified DESC");
|
||||
} //forum_get_discussions_fast
|
||||
|
||||
//reworked faster version from /mod/forum/lib.php
|
||||
function forum_get_child_posts_fast($parent, $forumid) {
|
||||
/**
|
||||
* reworked faster version from /mod/forum/lib.php
|
||||
* @param parent the id of the first post within the discussion
|
||||
* @param forum_id the forum identifier
|
||||
* @return an array of posts
|
||||
*/
|
||||
function forum_get_child_posts_fast($parent, $forum_id) {
|
||||
global $CFG;
|
||||
|
||||
$query = "
|
||||
SELECT
|
||||
p.id,
|
||||
p.subject,
|
||||
p.discussion,
|
||||
p.message,
|
||||
p.created,
|
||||
{$forum_id} AS forum,
|
||||
p.userid,
|
||||
u.firstname,
|
||||
u.lastname
|
||||
FROM
|
||||
{$CFG->prefix}forum_posts p
|
||||
LEFT JOIN
|
||||
{$CFG->prefix}user u
|
||||
ON
|
||||
p.userid = u.id
|
||||
WHERE
|
||||
p.parent = '{$parent}'
|
||||
ORDER BY
|
||||
p.created ASC
|
||||
";
|
||||
return get_records_sql($query);
|
||||
} //forum_get_child_posts_fast
|
||||
|
||||
return get_recordset_sql("SELECT p.id, p.subject, p.discussion, p.message, p.deleted,
|
||||
$forumid AS forum, u.firstname, u.lastname
|
||||
FROM {$CFG->prefix}forum_posts p
|
||||
LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
|
||||
WHERE p.parent = '$parent'
|
||||
ORDER BY p.created ASC");
|
||||
} //forum_get_child_posts_fast
|
||||
/**
|
||||
* this function handles the access policy to contents indexed as searchable documents. If this
|
||||
* function does not exist, the search engine assumes access is allowed.
|
||||
* When this point is reached, we already know that :
|
||||
* - user is legitimate in the surrounding context
|
||||
* - user may be guest and guest access is allowed to the module
|
||||
* - the function may perform local checks within the module information logic
|
||||
* @param path the access path to the module script code
|
||||
* @param itemtype the information subclassing (usefull for complex modules, defaults to 'standard')
|
||||
* @param this_id the item id within the information class denoted by itemtype. In forums, this id
|
||||
* points out the individual post.
|
||||
* @param user the user record denoting the user who searches
|
||||
* @param group_id the current group used by the user when searching
|
||||
* @return true if access is allowed, false elsewhere
|
||||
*/
|
||||
function forum_check_text_access($path, $itemtype, $this_id, $user, $group_id){
|
||||
global $CFG;
|
||||
|
||||
include_once("{$CFG->dirroot}/{$path}/lib.php");
|
||||
|
||||
// get the glossary object and all related stuff
|
||||
$post = get_record('forum_posts', 'id', $this_id);
|
||||
$dicussion = get_record('forum_discussion', 'id', $post->discussion);
|
||||
$course = get_record('course', 'id', $discussion->course);
|
||||
$context_module = get_record('context', 'id', $context_id);
|
||||
$cm = get_record('course_modules', 'id', $context_module->instanceid);
|
||||
if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', $context_module)) return false;
|
||||
|
||||
// approval check : entries should be approved for being viewed, or belongs to the user
|
||||
if (!$post->mailed && !has_capability('mod/forum:viewhiddentimeposts')) return false;
|
||||
|
||||
// group check : entries should be in accessible groups
|
||||
$current_group = get_current_group($course->id);
|
||||
if ((groupmode($course, $cm) == SEPARATEGROUPS) && ($group_id != $current_group) && !has_capability('mod/forum:viewdiscussionsfromallgroups')) return false;
|
||||
|
||||
return true;
|
||||
} //forum_check_text_access
|
||||
|
||||
?>
|
|
@ -1,88 +1,235 @@
|
|||
<?php
|
||||
/* This document illustrates how easy it is to add a module to
|
||||
* the search index - the only modifications made were creating
|
||||
* this file, and adding the SEARCH_TYPE_GLOSSARY constant to
|
||||
* search/lib.php - everything else is automatically handled
|
||||
* by the indexer script.
|
||||
* */
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* Michael Champanis (mchampan) [cynnical@gmail.com]
|
||||
* review 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* document handling for glossary activity module
|
||||
* This file contains a mapping between a glossary entry and it's indexable counterpart,
|
||||
*
|
||||
* Functions for iterating and retrieving the necessary records are now also included
|
||||
* in this file, rather than mod/glossary/lib.php
|
||||
**/
|
||||
|
||||
require_once("$CFG->dirroot/search/documents/document.php");
|
||||
require_once("$CFG->dirroot/search/documents/document.php");
|
||||
|
||||
class GlossarySearchDocument extends SearchDocument {
|
||||
public function __construct(&$entry, $glossary_id, $course_id, $group_id) {
|
||||
// generic information; required
|
||||
$doc->docid = $entry['id'];
|
||||
$doc->title = $entry['concept'];
|
||||
$doc->date = $entry['timecreated'];
|
||||
/*
|
||||
* a class for representing searchable information
|
||||
*
|
||||
**/
|
||||
class GlossarySearchDocument extends SearchDocument {
|
||||
|
||||
/**
|
||||
* document constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(&$entry, $course_id, $context_id) {
|
||||
// generic information; required
|
||||
$doc->docid = $entry['id'];
|
||||
$doc->documenttype = SEARCH_TYPE_GLOSSARY;
|
||||
$doc->itemtype = 'standard';
|
||||
$doc->contextid = $context_id;
|
||||
|
||||
$user = get_recordset('user', 'id', $entry['userid'])->fields;
|
||||
$doc->title = $entry['concept'];
|
||||
$doc->date = $entry['timecreated'];
|
||||
|
||||
$doc->author = $user['firstname'].' '.$user['lastname'];
|
||||
$doc->contents = $entry['definition'];
|
||||
$doc->url = glossary_make_link($entry['id']);
|
||||
|
||||
// module specific information; optional
|
||||
$data->glossary = $glossary_id;
|
||||
|
||||
// construct the parent class
|
||||
parent::__construct($doc, $data, SEARCH_TYPE_GLOSSARY, $course_id, $group_id);
|
||||
if ($entry['userid'])
|
||||
$user = get_record('user', 'id', $entry['userid']);
|
||||
$doc->author = ($user ) ? $user->firstname.' '.$user->lastname : '' ;
|
||||
$doc->contents = strip_tags($entry['definition']);
|
||||
$doc->url = glossary_make_link($entry['id']);
|
||||
|
||||
// module specific information; optional
|
||||
$data->glossary = $entry['glossaryid'];
|
||||
|
||||
// construct the parent class
|
||||
parent::__construct($doc, $data, $course_id, -1, $entry['userid'], PATH_FOR_SEARCH_TYPE_GLOSSARY);
|
||||
} //constructor
|
||||
} //GlossarySearchDocument
|
||||
} //GlossarySearchDocument
|
||||
|
||||
function glossary_make_link($entry_id) {
|
||||
/*
|
||||
* a class for representing searchable information
|
||||
*
|
||||
**/
|
||||
class GlossaryCommentSearchDocument extends SearchDocument {
|
||||
|
||||
/**
|
||||
* document constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(&$entry, $glossary_id, $course_id, $context_id) {
|
||||
// generic information; required
|
||||
$doc->docid = $entry['id'];
|
||||
$doc->documenttype = SEARCH_TYPE_GLOSSARY;
|
||||
$doc->itemtype = 'comment';
|
||||
$doc->contextid = $context_id;
|
||||
|
||||
$doc->title = get_string('commenton', 'search') . ' ' . $entry['concept'];
|
||||
$doc->date = $entry['timemodified'];
|
||||
|
||||
if ($entry['userid'])
|
||||
$user = get_record('user', 'id', $entry['userid']);
|
||||
$doc->author = ($user ) ? $user->firstname.' '.$user->lastname : '' ;
|
||||
$doc->contents = strip_tags($entry['entrycomment']);
|
||||
$doc->url = glossary_make_link($entry['entryid']);
|
||||
|
||||
// module specific information; optional
|
||||
$data->glossary = $glossary_id;
|
||||
|
||||
// construct the parent class
|
||||
parent::__construct($doc, $data, $course_id, -1, $entry['userid'], PATH_FOR_SEARCH_TYPE_GLOSSARY);
|
||||
} //constructor
|
||||
} //GlossaryCommentSearchDocument
|
||||
|
||||
/**
|
||||
* constructs valid access links to information
|
||||
* @param entry_id the id of the glossary entry
|
||||
* @return a full featured link element as a string
|
||||
*/
|
||||
function glossary_make_link($entry_id) {
|
||||
global $CFG;
|
||||
|
||||
//links directly to entry
|
||||
//return $CFG->wwwroot.'/mod/glossary/showentry.php?eid='.$entry_id;
|
||||
// return $CFG->wwwroot.'/mod/glossary/showentry.php?eid='.$entry_id;
|
||||
|
||||
//preserve glossary pop-up, be careful where you place your ' and "s
|
||||
// TOO LONG URL
|
||||
// Suggestion : bounce on popup within the glossarie's showentry page
|
||||
// preserve glossary pop-up, be careful where you place your ' and "s
|
||||
//this function is meant to return a url that is placed between href='[url here]'
|
||||
return "$CFG->wwwroot/mod/glossary/showentry.php?eid=$entry_id' onclick='return openpopup(\"/mod/glossary/showentry.php?eid=$entry_id\", \"entry\", \"menubar=0,location=0,scrollbars,resizable,width=600,height=450\", 0);";
|
||||
} //glossary_make_link
|
||||
return "$CFG->wwwroot/mod/glossary/showentry.php?eid=$entry_id' onclick='return openpopup(\"/mod/glossary/showentry.php?eid=$entry_id\", \"entry\", DEFAULT_POPUP_SETTINGS, 0);";
|
||||
} //glossary_make_link
|
||||
|
||||
function glossary_iterator() {
|
||||
return get_all_instances_in_courses("glossary", get_courses());
|
||||
} //glossary_iterator
|
||||
/**
|
||||
* part of search engine API
|
||||
*
|
||||
*/
|
||||
function glossary_iterator() {
|
||||
$glossaries = get_records('glossary');
|
||||
return $glossaries;
|
||||
} //glossary_iterator
|
||||
|
||||
/**
|
||||
* part of search engine API
|
||||
* @glossary a glossary instance
|
||||
* @return an array of searchable documents
|
||||
*/
|
||||
function glossary_get_content_for_index(&$glossary) {
|
||||
|
||||
// get context
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'glossary');
|
||||
$cm = get_record('course_modules', 'course', $glossary->course, 'module', $coursemodule, 'instance', $glossary->id);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
|
||||
function glossary_get_content_for_index(&$glossary) {
|
||||
$documents = array();
|
||||
|
||||
$entries = get_recordset('glossary_entries', 'glossaryid', $glossary->id);
|
||||
|
||||
while (!$entries->EOF) {
|
||||
$entry = $entries->fields;
|
||||
|
||||
if ($entry and strlen($entry['definition']) > 0) {
|
||||
$documents[] = new GlossarySearchDocument($entry, $glossary->id, $glossary->course, -1);
|
||||
} //if
|
||||
|
||||
$entries->MoveNext();
|
||||
} //foreach
|
||||
|
||||
$entryIds = array();
|
||||
// index entries
|
||||
$entries = get_records('glossary_entries', 'glossaryid', $glossary->id);
|
||||
if ($entries){
|
||||
foreach($entries as $entry) {
|
||||
$concepts[$entry->id] = $entry->concept;
|
||||
if (strlen($entry->definition) > 0) {
|
||||
$entryIds[] = $entry->id;
|
||||
$documents[] = new GlossarySearchDocument(get_object_vars($entry), $glossary->course, $context->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// index comments
|
||||
if (count($entryIds)){
|
||||
$entryIdList = implode(',', $entryIds);
|
||||
$comments = get_records_list('glossary_comments', 'entryid', $entryIdList);
|
||||
if ($comments){
|
||||
foreach($comments as $comment) {
|
||||
if (strlen($comment->entrycomment) > 0) {
|
||||
$comment->concept = $concepts[$comment->entryid];
|
||||
$documents[] = new GlossaryCommentSearchDocument(get_object_vars($comment), $glossary->id, $glossary->course, $context->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $documents;
|
||||
} //glossary_get_content_for_index
|
||||
} //glossary_get_content_for_index
|
||||
|
||||
//returns a single glossary search document based on a glossary_entry id
|
||||
function glossary_single_document($id) {
|
||||
$entries = get_recordset('glossary_entries', 'id', $id);
|
||||
$entry = $entries->fields;
|
||||
/**
|
||||
* part of search engine API
|
||||
* @param id the glossary entry identifier
|
||||
* @itemtype the type of information
|
||||
* @return a single search document based on a glossary entry
|
||||
*/
|
||||
function glossary_single_document($id, $itemtype) {
|
||||
if ($itemtype == 'standard'){
|
||||
$entry = get_record('glossary_entries', 'id', $id);
|
||||
}
|
||||
elseif ($itemtype == 'comment'){
|
||||
$comment = get_record('glossary_comments', 'id', $id);
|
||||
$entry = get_record('glossary_entries', 'id', $comment->entryid);
|
||||
}
|
||||
$glossary_course = get_field('glossary', 'course', 'id', $entry->glossaryid);
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'glossary');
|
||||
$cm = get_record('course_modules', 'course', $glossary_course, 'module', $coursemodule, 'instance', $entry->glossaryid);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
if ($itemtype == 'standard'){
|
||||
return new GlossarySearchDocument(get_object_vars($entry), $glossary_course, $context->id);
|
||||
}
|
||||
elseif ($itemtype == 'comment'){
|
||||
return new GlossaryCommentSearchDocument(get_object_vars($comment), $entry->glossaryid, $glossary_course, $context->id);
|
||||
}
|
||||
} //glossary_single_document
|
||||
|
||||
$glossaries = get_recordset('glossary', 'id', $entry['glossaryid']);
|
||||
$glossary = $glossaries->fields;
|
||||
/**
|
||||
* dummy delete function that packs id with itemtype.
|
||||
* this was here for a reason, but I can't remember it at the moment.
|
||||
*
|
||||
*/
|
||||
function glossary_delete($info, $itemtype) {
|
||||
$object->id = $info;
|
||||
$object->itemtype = $itemtype;
|
||||
return $object;
|
||||
} //glossary_delete
|
||||
|
||||
return new GlossarySearchDocument($entry, $entry['glossaryid'], $glossary['course'], -1);
|
||||
} //glossary_single_document
|
||||
|
||||
//dummy delete function that converts docid from the search table to itself..
|
||||
//this was here for a reason, but I can't remember it at the moment.
|
||||
function glossary_delete($info) {
|
||||
return $info;
|
||||
} //glossary_delete
|
||||
|
||||
//returns the var names needed to build a sql query for addition/deletions
|
||||
function glossary_db_names() {
|
||||
/**
|
||||
* returns the var names needed to build a sql query for addition/deletions
|
||||
*
|
||||
*/
|
||||
function glossary_db_names() {
|
||||
//[primary id], [table name], [time created field name], [time modified field name]
|
||||
return array('id', 'glossary_entries', 'timecreated', 'timemodified');
|
||||
} //glossary_db_names
|
||||
return array(
|
||||
array('id', 'glossary_entries', 'timecreated', 'timemodified', 'standard'),
|
||||
array('id', 'glossary_comments', 'timemodified', 'timemodified', 'comment')
|
||||
);
|
||||
} //glossary_db_names
|
||||
|
||||
/**
|
||||
* this function handles the access policy to contents indexed as searchable documents. If this
|
||||
* function does not exist, the search engine assumes access is allowed.
|
||||
* When this point is reached, we already know that :
|
||||
* - user is legitimate in the surrounding context
|
||||
* - user may be guest and guest access is allowed to the module
|
||||
* - the function may perform local checks within the module information logic
|
||||
* @param path the access path to the module script code
|
||||
* @param itemtype the information subclassing (usefull for complex modules, defaults to 'standard')
|
||||
* @param this_id the item id within the information class denoted by itemtype. In glossaries, this id
|
||||
* points out the indexed glossary item.
|
||||
* @param user the user record denoting the user who searches
|
||||
* @param group_id the current group used by the user when searching
|
||||
* @return true if access is allowed, false elsewhere
|
||||
*/
|
||||
function glossary_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
|
||||
global $CFG;
|
||||
|
||||
// get the glossary object and all related stuff
|
||||
$entry = get_record('glossary_entries', 'id', $id);
|
||||
$glossary = get_record('glossary', 'id', $entry->glossaryid);
|
||||
$course = get_record('course', 'id', $glossary->course);
|
||||
$module_context = get_record('context', 'id', $context_id);
|
||||
$cm = get_record('course_modules', 'id', $module_context->instance);
|
||||
if (!$cm->visible && !has_capability('moodle/course:viewhiddenactivities', $module_context)) return false;
|
||||
|
||||
//approval check : entries should be approved for being viewed, or belongs to the user unless the viewer can approve them or manage them
|
||||
if (!$entry->approved && $user != $entry->userid && !has_capability('mod/glossary:approve', $module_context) && !has_capability('mod/glossary:manageentries', $module_context)) return false;
|
||||
|
||||
return true;
|
||||
} //glossary_check_text_access
|
||||
|
||||
?>
|
47
search/documents/physical_doc.php
Normal file
47
search/documents/physical_doc.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* add-on 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* this is a format handler for getting text out of a proprietary binary format
|
||||
* so it can be indexed by Lucene search engine
|
||||
*/
|
||||
|
||||
/*
|
||||
* MS Word extractor
|
||||
*/
|
||||
|
||||
function get_text_for_indexing_doc(&$resource){
|
||||
global $CFG, $USER;
|
||||
|
||||
// SECURITY : do not allow non admin execute anything on system !!
|
||||
if (!isadmin($USER->id)) return;
|
||||
|
||||
// just call pdftotext over stdout and capture the output
|
||||
if (!empty($CFG->block_search_word_to_text_cmd)){
|
||||
if (!file_exists("{$CFG->dirroot}/{$CFG->block_search_word_to_text_cmd}")){
|
||||
mtrace('Error with MSWord to text converter command : exectuable not found.');
|
||||
}
|
||||
else{
|
||||
$file = $CFG->dataroot.'/'.$resource->course.'/'.$resource->reference;
|
||||
$text_converter_cmd = "{$CFG->dirroot}/{$CFG->block_search_word_to_text_cmd} $file";
|
||||
if ($CFG->block_search_word_to_text_env){
|
||||
putenv($CFG->block_search_word_to_text_env);
|
||||
}
|
||||
$result = shell_exec($text_converter_cmd);
|
||||
if ($result){
|
||||
return mb_convert_encoding($result, 'UTF8', 'auto');
|
||||
}
|
||||
else{
|
||||
mtrace('Error with MSWord to text converter command : execution failed.');
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
mtrace('Error with MSWord to text converter command : command not set up. Execute once search block configuration.');
|
||||
return '';
|
||||
}
|
||||
}
|
||||
?>
|
39
search/documents/physical_htm.php
Normal file
39
search/documents/physical_htm.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* add-on 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* this is a format handler for getting text out of a proprietary binary format
|
||||
* so it can be indexed by Lucene search engine
|
||||
*/
|
||||
|
||||
function get_text_for_indexing_htm(&$resource){
|
||||
global $CFG, $USER;
|
||||
|
||||
// SECURITY : do not allow non admin execute anything on system !!
|
||||
if (!isadmin($USER->id)) return;
|
||||
|
||||
// just get text
|
||||
$text = implode('', file("{$CFG->dataroot}/{$resource->course}/($resource->reference)"));
|
||||
|
||||
// extract keywords and other interesting meta information and put it back as real content for indexing
|
||||
if (preg_match('/(.*)<meta ([^>]*)>(.*)/is',$text, $matches)){
|
||||
$prefix = $matches[1];
|
||||
$meta_attributes = $matches[2];
|
||||
$suffix = $matches{3];
|
||||
if (preg_match('/name="(keywords|description)"/i', $attributes)){
|
||||
preg_match('/content="[^"]+"/i', $attributes, $matches);
|
||||
$text = $prefix.' '.$matches[1].' '.$suffix;
|
||||
}
|
||||
}
|
||||
// filter all html tags
|
||||
// $text = clean_text($text, FORMAT_PLAIN);
|
||||
// NOTE : this is done in ResourceSearchDocument __constructor
|
||||
|
||||
if (!empty($CFG->block_search_limit_index_body)){
|
||||
$text = shorten($text, $CFG->block_search_limit_index_body);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
?>
|
17
search/documents/physical_html.php
Normal file
17
search/documents/physical_html.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* add-on 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* this is a format handler for getting text out of a proprietary binary format
|
||||
* so it can be indexed by Lucene search engine
|
||||
*/
|
||||
|
||||
function get_text_for_indexing_html(&$resource){
|
||||
|
||||
// wraps to htm handler
|
||||
include_once 'physical_htm.php';
|
||||
return get_text_for_indexing_htm($resource);
|
||||
}
|
||||
?>
|
41
search/documents/physical_pdf.php
Normal file
41
search/documents/physical_pdf.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* add-on 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* this is a format handler for getting text out of a proprietary binary format
|
||||
* so it can be indexed by Lucene search engine
|
||||
*/
|
||||
|
||||
function get_text_for_indexing_pdf(&$resource){
|
||||
global $CFG, $USER;
|
||||
|
||||
// SECURITY : do not allow non admin execute anything on system !!
|
||||
if (!isadmin($USER->id)) return;
|
||||
|
||||
// just call pdftotext over stdout and capture the output
|
||||
if (!empty($CFG->block_search_pdf_to_text_cmd)){
|
||||
preg_match("/^\S+/", $CFG->block_search_pdf_to_text_cmd, $matches);
|
||||
if (!file_exists("{$CFG->dirroot}/{$matches[0]}")){
|
||||
mtrace('Error with pdf to text converter command : exectuable not found.');
|
||||
}
|
||||
else{
|
||||
$file = $CFG->dataroot.'/'.$resource->course.'/'.$resource->reference;
|
||||
$text_converter_cmd = "{$CFG->dirroot}/{$CFG->block_search_pdf_to_text_cmd} $file -";
|
||||
$result = shell_exec($text_converter_cmd);
|
||||
if ($result){
|
||||
return $result;
|
||||
}
|
||||
else{
|
||||
mtrace('Error with pdf to text converter command : execution failed.');
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
mtrace('Error with pdf to text converter command : command not set up. Execute once search block configuration.');
|
||||
return '';
|
||||
}
|
||||
}
|
||||
?>
|
80
search/documents/physical_ppt.php
Normal file
80
search/documents/physical_ppt.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* add-on 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* this is a format handler for getting text out of a proprietary binary format
|
||||
* so it can be indexed by Lucene search engine
|
||||
*/
|
||||
|
||||
/**
|
||||
* first implementation is a trivial heuristic based on ppt character stream :
|
||||
* text sequence always starts with a 00 9F 0F 04 sequence followed by a 15 bytes
|
||||
* sequence
|
||||
* In this sequence is a A8 0F or A0 0F or AA 0F followed by a little-indian encoding of text buffer size
|
||||
* A8 0F denotes for ASCII text (local system monobyte encoding)
|
||||
* A0 0F denotes for UTF-16 encoding
|
||||
* AA 0F are non textual sequences
|
||||
* texts are either in ASCII or UTF-16
|
||||
* text ends on a new sequence start, or on a 00 00 NULL UTF-16 end of stream
|
||||
*
|
||||
* based on these following rules, here is a little empiric texte extractor for PPT
|
||||
*/
|
||||
|
||||
function get_text_for_indexing_ppt(&$resource){
|
||||
global $CFG, $USER;
|
||||
|
||||
$indextext = null;
|
||||
|
||||
// SECURITY : do not allow non admin execute anything on system !!
|
||||
if (!isadmin($USER->id)) return;
|
||||
|
||||
$text = implode('', file("{$CFG->dataroot}/{$resource->course}/{$resource->reference}"));
|
||||
|
||||
$remains = $text;
|
||||
$fragments = array();
|
||||
while (preg_match('/\x00\x9F\x0F\x04.{9}(......)(.*)/s', $remains, $matches)){
|
||||
$unpacked = unpack("ncode/Llength", $matches[1]);
|
||||
$sequencecode = $unpacked['code'];
|
||||
$length = $unpacked['length'];
|
||||
// print "length : ".$length." ; segment type : ".sprintf("%x", $sequencecode)."<br/>";
|
||||
$followup = $matches[2];
|
||||
// local system encoding sequence
|
||||
if ($sequencecode == 0xA80F){
|
||||
$aFragment = substr($followup, 0, $length);
|
||||
$remains = substr($followup, $length);
|
||||
$fragments[] = $aFragment;
|
||||
}
|
||||
// denotes unicode encoded sequence
|
||||
elseif ($sequencecode == 0xA00F){
|
||||
$aFragment = substr($followup, 0, $length);
|
||||
// $aFragment = mb_convert_encoding($aFragment, 'UTF-16', 'UTF-8');
|
||||
$aFragment = preg_replace('/\xA0\x00\x19\x20/s', "'", $aFragment); // some quotes
|
||||
$aFragment = preg_replace('/\x00/s', "", $aFragment);
|
||||
$remains = substr($followup, $length);
|
||||
$fragments[] = $aFragment;
|
||||
}
|
||||
else{
|
||||
$remains = $followup;
|
||||
}
|
||||
}
|
||||
$indextext = implode(' ', $fragments);
|
||||
$indextext = preg_replace('/\x19\x20/', "'", $indextext); // some quotes
|
||||
$indextext = preg_replace('/\x09/', '', $indextext); // some extra chars
|
||||
$indextext = preg_replace('/\x0D/', "\n", $indextext); // some quotes
|
||||
$indextext = preg_replace('/\x0A/', "\n", $indextext); // some quotes
|
||||
$indextextprint = implode('<hr/>', $fragments);
|
||||
|
||||
$logppt = fopen("C:/php5/logs/pptlog", "w");
|
||||
fwrite($logppt, $indextext);
|
||||
fclose($logppt);
|
||||
|
||||
if (!empty($CFG->block_search_limit_index_body)){
|
||||
$indextext = shorten($text, $CFG->block_search_limit_index_body);
|
||||
}
|
||||
|
||||
$indextext = mb_convert_encoding($indextext, 'UTF8', 'auto');
|
||||
return $indextext;
|
||||
}
|
||||
?>
|
24
search/documents/physical_txt.php
Normal file
24
search/documents/physical_txt.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* add-on 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* this is a format handler for getting text out of a proprietary binary format
|
||||
* so it can be indexed by Lucene search engine
|
||||
*/
|
||||
|
||||
function get_text_for_indexing_txt(&$resource){
|
||||
global $CFG, $USER;
|
||||
|
||||
// SECURITY : do not allow non admin execute anything on system !!
|
||||
if (!isadmin($USER->id)) return;
|
||||
|
||||
// just try to get text empirically from ppt binary flow
|
||||
$text = implode('', file("{$CFG->dataroot}/{$resource->course}/{$resource->reference}"));
|
||||
if (!empty($CFG->block_search_limit_index_body)){
|
||||
$text = shorten($text, $CFG->block_search_limit_index_body);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
?>
|
28
search/documents/physical_xml.php
Normal file
28
search/documents/physical_xml.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* add-on 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* this is a format handler for getting text out of a proprietary binary format
|
||||
* so it can be indexed by Lucene search engine
|
||||
*/
|
||||
|
||||
function get_text_for_indexing_xml(&$resource){
|
||||
global $CFG, $USER;
|
||||
|
||||
// SECURITY : do not allow non admin execute anything on system !!
|
||||
if (!isadmin($USER->id)) return;
|
||||
|
||||
// just get text
|
||||
$text = implode('', file("{$CFG->dataroot}/{$resource->course}/($resource->reference)"));
|
||||
|
||||
// filter out all xml tags
|
||||
$text = preg_replace("/<[^>]*>/", ' ', $text);
|
||||
|
||||
if (!empty($CFG->block_search_limit_index_body)){
|
||||
$text = shorten($text, $CFG->block_search_limit_index_body);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
?>
|
|
@ -1,86 +1,307 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* Michael Champanis (mchampan) [cynnical@gmail.com]
|
||||
* review 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* document handling for all resources
|
||||
* This file contains the mapping between a resource and it's indexable counterpart,
|
||||
*
|
||||
* Functions for iterating and retrieving the necessary records are now also included
|
||||
* in this file, rather than mod/resource/lib.php
|
||||
**/
|
||||
|
||||
require_once("$CFG->dirroot/search/documents/document.php");
|
||||
require_once("$CFG->dirroot/search/documents/document.php");
|
||||
require_once("$CFG->dirroot/mod/resource/lib.php");
|
||||
|
||||
class ResourceSearchDocument extends SearchDocument {
|
||||
public function __construct(&$resource) {
|
||||
// generic information; required
|
||||
$doc->docid = $resource['id'];
|
||||
$doc->title = strip_tags($resource['name']);
|
||||
$doc->date = $resource['timemodified'];
|
||||
/*
|
||||
* a class for representing searchable information
|
||||
*
|
||||
**/
|
||||
class ResourceSearchDocument extends SearchDocument {
|
||||
public function __construct(&$resource, $context_id) {
|
||||
// generic information; required
|
||||
$doc->docid = $resource['trueid'];
|
||||
$doc->documenttype = SEARCH_TYPE_RESOURCE;
|
||||
$doc->itemtype = $resource['type'];
|
||||
$doc->contextid = $context_id;
|
||||
|
||||
$doc->author = '';
|
||||
$doc->contents = strip_tags($resource['summary']).' '.strip_tags($resource['alltext']);
|
||||
$doc->url = resource_make_link($resource['id']);
|
||||
|
||||
// module specific information; optional
|
||||
$data = array();
|
||||
|
||||
// construct the parent class
|
||||
parent::__construct($doc, $data, SEARCH_TYPE_RESOURCE, $resource['course'], -1);
|
||||
$doc->title = strip_tags($resource['name']);
|
||||
$doc->date = $resource['timemodified'];
|
||||
$doc->author = '';
|
||||
$doc->contents = strip_tags($resource['summary']).' '.strip_tags($resource['alltext']);
|
||||
$doc->url = resource_make_link($resource['id']);
|
||||
|
||||
// module specific information; optional
|
||||
$data = array();
|
||||
|
||||
// construct the parent class
|
||||
parent::__construct($doc, $data, $resource['course'], 0, 0, PATH_FOR_SEARCH_TYPE_RESOURCE);
|
||||
} //constructor
|
||||
} //ResourceSearchDocument
|
||||
} //ResourceSearchDocument
|
||||
|
||||
function resource_make_link($resource_id) {
|
||||
/**
|
||||
* constructs valid access links to information
|
||||
* @param resourceId the of the resource
|
||||
* @return a full featured link element as a string
|
||||
*/
|
||||
function resource_make_link($resource_id) {
|
||||
global $CFG;
|
||||
return $CFG->wwwroot.'/mod/resource/view.php?r='.$resource_id;
|
||||
} //resource_make_link
|
||||
|
||||
return $CFG->wwwroot.'/mod/resource/view.php?id='.$resource_id;
|
||||
} //resource_make_link
|
||||
|
||||
function resource_iterator() {
|
||||
/**
|
||||
* part of standard API
|
||||
*
|
||||
*/
|
||||
function resource_iterator() {
|
||||
//trick to leave search indexer functionality intact, but allow
|
||||
//this document to only use the below function to return info
|
||||
//to be searched
|
||||
return array(true);
|
||||
} //resource_iterator
|
||||
|
||||
//this function does not need a content iterator, returns all the info
|
||||
//itself; remember to fake the iterator array though
|
||||
function resource_get_content_for_index(&$notneeded) {
|
||||
/**
|
||||
* part of standard API
|
||||
* this function does not need a content iterator, returns all the info
|
||||
* itself;
|
||||
* @param notneeded to comply API, remember to fake the iterator array though
|
||||
* @return an array of searchable documents
|
||||
*/
|
||||
function resource_get_content_for_index(&$notneeded) {
|
||||
global $CFG;
|
||||
|
||||
// starting with Moodle native resources
|
||||
$documents = array();
|
||||
$query = "
|
||||
SELECT
|
||||
id as trueid,
|
||||
r.*
|
||||
FROM
|
||||
{$CFG->prefix}resource as r
|
||||
WHERE
|
||||
alltext != '' AND
|
||||
alltext != ' ' AND
|
||||
alltext != ' ' AND
|
||||
type != 'file'
|
||||
";
|
||||
$resources = get_records_sql($query);
|
||||
|
||||
$resources = get_recordset_sql('SELECT *
|
||||
FROM {$CFG->prefix}resource
|
||||
WHERE alltext NOT LIKE ""
|
||||
AND alltext NOT LIKE " "
|
||||
AND alltext NOT LIKE " "
|
||||
AND TYPE != "file"');
|
||||
|
||||
while (!$resources->EOF) {
|
||||
$resource = $resources->fields;
|
||||
|
||||
if ($resource) {
|
||||
$documents[] = new ResourceSearchDocument($resource);
|
||||
} //if
|
||||
|
||||
$resources->MoveNext();
|
||||
} //foreach
|
||||
foreach($resources as $aResource){
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'resource');
|
||||
$cm = get_record('course_modules', 'course', $aResource->course, 'module', $coursemodule, 'instance', $aResource->id);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
$aResource->id = $cm->id;
|
||||
$documents[] = new ResourceSearchDocument(get_object_vars($aResource), $context->id);
|
||||
mtrace("finished $aResource->name");
|
||||
}
|
||||
|
||||
// special physical files handling
|
||||
/**
|
||||
* this sequence searches for a compatible physical stream handler for getting a text
|
||||
* equivalence for the content.
|
||||
*
|
||||
*/
|
||||
if (@$CFG->block_search_enable_file_indexing){
|
||||
$query = "
|
||||
SELECT
|
||||
r.id as trueid,
|
||||
cm.id as id,
|
||||
r.course as course,
|
||||
r.name as name,
|
||||
r.summary as summary,
|
||||
r.alltext as alltext,
|
||||
r.reference as reference,
|
||||
r.type as type,
|
||||
r.timemodified as timemodified
|
||||
FROM
|
||||
{$CFG->prefix}resource as r,
|
||||
{$CFG->prefix}course_modules as cm,
|
||||
{$CFG->prefix}modules as m
|
||||
WHERE
|
||||
r.type = 'file' AND
|
||||
cm.instance = r.id AND
|
||||
cm.course = r.course AND
|
||||
cm.module = m.id AND
|
||||
m.name = 'resource'
|
||||
";
|
||||
$resources = get_records_sql($query);
|
||||
|
||||
// invokes external content extractor if exists.
|
||||
foreach($resources as $aResource){
|
||||
// fetches a physical indexable document and adds it to documents passed by ref
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'resource');
|
||||
$cm = get_record('course_modules', 'id', $aResource->id);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
resource_get_physical_file($aResource, $context->id, false, $documents);
|
||||
}
|
||||
}
|
||||
return $documents;
|
||||
} //resource_get_content_for_index
|
||||
} //resource_get_content_for_index
|
||||
|
||||
//returns a single resource search document based on a resource_entry id
|
||||
function resource_single_document($id) {
|
||||
$resources = get_recordset_sql('SELECT *
|
||||
FROM {$CFG->prefix}resource
|
||||
WHERE alltext NOT LIKE ""
|
||||
AND alltext NOT LIKE " "
|
||||
AND alltext NOT LIKE " "
|
||||
AND TYPE != "file",
|
||||
AND id = '.$id);
|
||||
/**
|
||||
* get text from a physical file
|
||||
* @param resource a resource for which to fetch some representative text
|
||||
* @param getsingle if true, returns a single search document, elsewhere return the array
|
||||
* given as documents increased by one
|
||||
* @param documents the array of documents, by ref, where to add the new document.
|
||||
* @return a search document when unique or false.
|
||||
*/
|
||||
function resource_get_physical_file(&$resource, $context_id, $getsingle, &$documents = null){
|
||||
global $CFG;
|
||||
|
||||
// cannot index empty references
|
||||
if (empty($resource->reference)) return false;
|
||||
|
||||
$resource = $resources->fields;
|
||||
// cannot index remote resources
|
||||
if (resource_is_url($resource->reference)){
|
||||
mtrace("Cannot index remote URLs.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return new ResourceSearchDocument($resource);
|
||||
} //resource_single_document
|
||||
$fileparts = pathinfo($resource->reference);
|
||||
// cannot index unknown or masked types
|
||||
if (empty($fileparts['extension'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// cannot index non existent file
|
||||
$file = "{$CFG->dataroot}/{$resource->course}/{$resource->reference}";
|
||||
if (!file_exists($file)){
|
||||
mtrace("Missing resource file $file : will not be indexed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
$ext = strtolower($fileparts['extension']);
|
||||
|
||||
function resource_delete($info) {
|
||||
return $info;
|
||||
} //resource_delete
|
||||
// cannot index unallowed or unhandled types
|
||||
if (!preg_match("/\b$ext\b/i", $CFG->block_search_filetypes)) {
|
||||
mtrace($fileparts['extension'] . ' is not an allowed extension for indexing');
|
||||
return false;
|
||||
}
|
||||
if (file_exists($CFG->dirroot.'/search/documents/physical_'.$ext.'.php')){
|
||||
include_once($CFG->dirroot.'/search/documents/physical_'.$ext.'.php');
|
||||
$function_name = 'get_text_for_indexing_'.$ext;
|
||||
$resource->alltext = $function_name($resource);
|
||||
if (!empty($resource->alltext)){
|
||||
if ($getsingle){
|
||||
return new ResourceSearchDocument(get_object_vars($resource));
|
||||
}
|
||||
else{
|
||||
$documents[] = new ResourceSearchDocument(get_object_vars($resource), $context_id);
|
||||
}
|
||||
mtrace("finished file $resource->name as {$resource->reference}");
|
||||
}
|
||||
}
|
||||
else{
|
||||
mtrace("fulltext handler not found for $ext type");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//returns the var names needed to build a sql query for addition/deletions
|
||||
function resource_db_names() {
|
||||
/**
|
||||
* part of standard API.
|
||||
* returns a single resource search document based on a resource_entry id
|
||||
* @param id the id of the accessible document
|
||||
* @return a searchable object or null if failure
|
||||
*/
|
||||
function resource_single_document($id, $itemtype) {
|
||||
global $CFG;
|
||||
|
||||
// rewriting with legacy moodle databse API
|
||||
$query = "
|
||||
SELECT
|
||||
r.id as trueid,
|
||||
cm.id as id,
|
||||
r.course as course,
|
||||
r.name as name,
|
||||
r.summary as summary,
|
||||
r.alltext as alltext,
|
||||
r.reference as reference,
|
||||
r.type as type,
|
||||
r.timemodified as timemodified
|
||||
FROM
|
||||
{$CFG->prefix}resource as r,
|
||||
{$CFG->prefix}course_modules as cm,
|
||||
{$CFG->prefix}modules as m
|
||||
WHERE
|
||||
cm.instance = r.id AND
|
||||
cm.course = r.course AND
|
||||
cm.module = m.id AND
|
||||
m.name = 'resource' AND
|
||||
((r.type != 'file' AND
|
||||
r.alltext != '' AND
|
||||
r.alltext != ' ' AND
|
||||
r.alltext != ' ') OR
|
||||
r.type = 'file') AND
|
||||
r.id = '{$id}'
|
||||
";
|
||||
$resource = get_record_sql($query);
|
||||
|
||||
if ($resource){
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'resource');
|
||||
$cm = get_record('course_modules', 'id', $resource->id);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
if ($resource->type == 'file' && @$CFG->block_search_enable_file_indexing){
|
||||
$document = resource_get_physical_file($resource, true, $context->id);
|
||||
if (!$document) mtrace("Warning : this document {$resource->name} will not be indexed");
|
||||
return $document;
|
||||
}
|
||||
else{
|
||||
return new ResourceSearchDocument(get_object_vars($resource), $context->id);
|
||||
}
|
||||
}
|
||||
mtrace("null resource");
|
||||
return null;
|
||||
} //resource_single_document
|
||||
|
||||
/**
|
||||
* dummy delete function that aggregates id with itemtype.
|
||||
* this was here for a reason, but I can't remember it at the moment.
|
||||
*
|
||||
*/
|
||||
function resource_delete($info, $itemtype) {
|
||||
$object->id = $info;
|
||||
$object->itemtype = $itemtype;
|
||||
return $object;
|
||||
} //resource_delete
|
||||
|
||||
/**
|
||||
* returns the var names needed to build a sql query for addition/deletions
|
||||
*
|
||||
*/
|
||||
function resource_db_names() {
|
||||
//[primary id], [table name], [time created field name], [time modified field name], [additional where conditions for sql]
|
||||
return array('id', 'resource', 'timemodified', 'timemodified', "WHERE alltext NOT LIKE '' AND alltext NOT LIKE ' ' AND alltext NOT LIKE ' ' AND TYPE != 'file'");
|
||||
} //resource_db_names
|
||||
return array(array('id', 'resource', 'timemodified', 'timemodified', '*', " (alltext != '' AND alltext != ' ' AND alltext != ' ' AND TYPE != 'file') OR TYPE = 'file' "));
|
||||
} //resource_db_names
|
||||
|
||||
/**
|
||||
* this function handles the access policy to contents indexed as searchable documents. If this
|
||||
* function does not exist, the search engine assumes access is allowed.
|
||||
* @param path the access path to the module script code
|
||||
* @param itemtype the information subclassing (usefull for complex modules, defaults to 'standard')
|
||||
* @param this_id the item id within the information class denoted by itemtype. In resources, this id
|
||||
* points to the resource record and not to the module that shows it.
|
||||
* @param user the user record denoting the user who searches
|
||||
* @param group_id the current group used by the user when searching
|
||||
* @return true if access is allowed, false elsewhere
|
||||
*/
|
||||
function resource_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
|
||||
global $CFG;
|
||||
|
||||
include_once("{$CFG->dirroot}/{$path}/lib.php");
|
||||
|
||||
$r = get_record('resource', 'id', $this_id);
|
||||
$module_context = get_record('context', 'id', $context_id);
|
||||
$cm = get_record('course_modules', 'id', $module_context->instance);
|
||||
|
||||
//check if found course module is visible
|
||||
if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', $module_context)){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} //resource_check_text_access
|
||||
?>
|
278
search/documents/techproject_document.php
Normal file
278
search/documents/techproject_document.php
Normal file
|
@ -0,0 +1,278 @@
|
|||
<?php
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* add-on 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* document handling for techproject activity module
|
||||
*/
|
||||
/* see wiki_document.php for descriptions */
|
||||
|
||||
require_once("$CFG->dirroot/search/documents/document.php");
|
||||
require_once("$CFG->dirroot/mod/techproject/lib.php");
|
||||
|
||||
/**
|
||||
* a class for representing searchable information
|
||||
*
|
||||
*/
|
||||
class TechprojectEntrySearchDocument extends SearchDocument {
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(&$entry, $course_id, $context_id) {
|
||||
// generic information
|
||||
$doc->docid = $entry['id'];
|
||||
$doc->documenttype = SEARCH_TYPE_TECHPROJECT;
|
||||
$doc->itemtype = $entry['entry_type'];
|
||||
$doc->contextid = $context_id;
|
||||
|
||||
|
||||
$doc->title = $entry['abstract'];
|
||||
$doc->author = ($entry['userid']) ? $entry['author'] : '';
|
||||
$doc->contents = strip_tags($entry['description']);
|
||||
$doc->date = '';
|
||||
|
||||
$doc->url = techproject_make_link($entry['projectid'], $entry['id'], $entry['entry_type'], $entry['groupid']);
|
||||
|
||||
// module specific information
|
||||
$data->techproject = $entry['projectid'];
|
||||
|
||||
parent::__construct($doc, $data, $course_id, $entry['groupid'], $entry['userid'], PATH_FOR_SEARCH_TYPE_TECHPROJECT);
|
||||
} //constructor
|
||||
} //TechprojectEntrySearchDocument
|
||||
|
||||
/**
|
||||
* constructs a valid link to a description detail
|
||||
*
|
||||
*/
|
||||
function techproject_make_link($techproject_id, $entry_id, $entry_type, $group_id) {
|
||||
global $CFG;
|
||||
return $CFG->wwwroot.'/mod/techproject/view.php?view=view_detail&id='.$techproject_id.'&objectId='.$entry_id.'&objectClass='.$entry_type.'&group='.$group_id;
|
||||
} //techproject_make_link
|
||||
|
||||
/**
|
||||
* search standard API
|
||||
*
|
||||
*/
|
||||
function techproject_iterator() {
|
||||
$techprojects = get_records('techproject');
|
||||
return $techprojects;
|
||||
} //techproject_iterator
|
||||
|
||||
/**
|
||||
* search standard API
|
||||
* @param techproject a techproject instance
|
||||
* @return an array of collected searchable documents
|
||||
*/
|
||||
function techproject_get_content_for_index(&$techproject) {
|
||||
$documents = array();
|
||||
if (!$techproject) return $documents;
|
||||
|
||||
$requirements = techproject_get_entries($techproject->id, 'requirement');
|
||||
$specifications = techproject_get_entries($techproject->id, 'specification');
|
||||
$tasks = techproject_get_tasks($techproject->id);
|
||||
$milestones = techproject_get_entries($techproject->id, 'milestone');
|
||||
$deliverables = techproject_get_entries($techproject->id, 'deliverable');
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'techproject');
|
||||
$cm = get_record('course_modules', 'course', $techproject->course, 'module', $coursemodule, 'instance', $techproject->id);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
|
||||
$entries = array_merge($requirements, $specifications, $milestones, $deliverables);
|
||||
foreach($entries as $anEntry) {
|
||||
if ($anEntry) {
|
||||
if (strlen($anEntry->description) > 0) {
|
||||
$documents[] = new TechprojectEntrySearchDocument(get_object_vars($anEntry), $techproject->course, $context->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($tasks as $aTask) {
|
||||
if ($aTask) {
|
||||
if (strlen($aTask->description) > 0) {
|
||||
if ($aTask->assignee){
|
||||
$user = get_record('user', 'id', $aTask->assignee);
|
||||
$aTask->author = $user->firstname.' '.$user->lastname;
|
||||
}
|
||||
$documents[] = new TechprojectEntrySearchDocument(get_object_vars($aTask), $techproject->course, $context->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $documents;
|
||||
} //techproject_get_content_for_index
|
||||
|
||||
/**
|
||||
* returns a single techproject search document based on a techproject_entry id and itemtype
|
||||
*
|
||||
*/
|
||||
function techproject_single_document($id, $itemtype) {
|
||||
switch ($itemtype){
|
||||
case 'requirement':{
|
||||
$entry = get_record('techproject_requirement', 'id', $id);
|
||||
break;
|
||||
}
|
||||
case 'specification':{
|
||||
$entry = get_record('techproject_specification', 'id', $id);
|
||||
break;
|
||||
}
|
||||
case 'milestone':{
|
||||
$entry = get_record('techproject_milestone', 'id', $id);
|
||||
break;
|
||||
}
|
||||
case 'deliverable':{
|
||||
$entry = get_record('techproject_deliverable', 'id', $id);
|
||||
break;
|
||||
}
|
||||
case 'task':{
|
||||
$entry = get_record('techproject_task', 'id', $id);
|
||||
if ($entry->assignee){
|
||||
$user = get_record('user', 'id', $entry->assignee);
|
||||
$entry->author = $user->firstname.' '.$user->lastname;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$techprojet_course = get_field('techproject', 'course', 'id', $entry->projectid);
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'techproject');
|
||||
$cm = get_record('course_modules', 'course', $techproject_course, 'module', $coursemodule, 'instance', $entry->projectid);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
$entry->type = $itemtype;
|
||||
$techproject = get_record('techproject', 'id', $requirement->projectid);
|
||||
return new TechprojectEntrySearchDocument(get_object_vars($anEntry), $techproject->course, $context->id);
|
||||
} //techproject_single_document
|
||||
|
||||
/**
|
||||
* dummy delete function that packs id with itemtype.
|
||||
* this was here for a reason, but I can't remember it at the moment.
|
||||
*
|
||||
*/
|
||||
function techproject_delete($info, $itemtype) {
|
||||
$object->id = $info;
|
||||
$object->itemtype = $itemtype;
|
||||
return $object;
|
||||
} //techproject_delete
|
||||
|
||||
/**
|
||||
* returns the var names needed to build a sql query for addition/deletions
|
||||
*
|
||||
*/
|
||||
// TODO : what should we do there ?
|
||||
function techproject_db_names() {
|
||||
//[primary id], [table name], [time created field name], [time modified field name]
|
||||
return array(
|
||||
array('id', 'techproject_requirement', 'created', 'modified', 'requirement'),
|
||||
array('id', 'techproject_specification', 'created', 'modified', 'specification'),
|
||||
array('id', 'techproject_task', 'created', 'modified', 'task'),
|
||||
array('id', 'techproject_milestone', 'created', 'modified', 'milestone'),
|
||||
array('id', 'techproject_deliverable', 'created', 'modified', 'deliverable')
|
||||
);
|
||||
} //techproject_db_names
|
||||
|
||||
/**
|
||||
* get a complete list of entries of one particular type
|
||||
* @param techprojectId the project instance
|
||||
* @param type the entity type
|
||||
* @return an array of records
|
||||
*/
|
||||
function techproject_get_entries($techproject_id, $type) {
|
||||
global $CFG;
|
||||
|
||||
$query = "
|
||||
SELECT
|
||||
e.id,
|
||||
e.abstract,
|
||||
e.description,
|
||||
e.projectid,
|
||||
e.groupid,
|
||||
e.userid,
|
||||
'$type' AS entry_type
|
||||
FROM
|
||||
{$CFG->prefix}techproject_{$type} AS e
|
||||
WHERE
|
||||
e.projectid = '{$techproject_id}'
|
||||
";
|
||||
return get_records_sql($query);
|
||||
} //techproject_get_entries
|
||||
|
||||
/**
|
||||
* get the task list for a project instance
|
||||
* @param techprojectId the project
|
||||
* @return an array of records that represent tasks
|
||||
*/
|
||||
function techproject_get_tasks($techproject_id) {
|
||||
global $CFG;
|
||||
|
||||
$query = "
|
||||
SELECT
|
||||
t.id,
|
||||
t.abstract,
|
||||
t.description,
|
||||
t.projectid,
|
||||
t.groupid,
|
||||
t.owner as userid,
|
||||
u.firstname,
|
||||
u.lastname,
|
||||
'task' as entry_type
|
||||
FROM
|
||||
{$CFG->prefix}techproject_task AS t
|
||||
LEFT JOIN
|
||||
{$CFG->prefix}user AS u
|
||||
ON
|
||||
t.owner = u.id
|
||||
WHERE
|
||||
t.projectid = '{$techproject_id}'
|
||||
ORDER BY
|
||||
t.taskstart ASC
|
||||
";
|
||||
return get_records_sql($query);
|
||||
} //techproject_get_tasks
|
||||
|
||||
/**
|
||||
* this function handles the access policy to contents indexed as searchable documents. If this
|
||||
* function does not exist, the search engine assumes access is allowed.
|
||||
* When this point is reached, we already know that :
|
||||
* - user is legitimate in the surrounding context
|
||||
* - user may be guest and guest access is allowed to the module
|
||||
* - the function may perform local checks within the module information logic
|
||||
* @param path the access path to the module script code
|
||||
* @param entry_type the information subclassing (usefull for complex modules, defaults to 'standard')
|
||||
* @param this_id the item id within the information class denoted by entry_type. In techprojects, this id
|
||||
* points to the techproject instance in which all resources are indexed.
|
||||
* @param user the user record denoting the user who searches
|
||||
* @param group_id the current group used by the user when searching
|
||||
* @return true if access is allowed, false elsewhere
|
||||
*/
|
||||
function techproject_check_text_access($path, $entry_type, $this_id, $user, $group_id, $context_id){
|
||||
global $CFG;
|
||||
|
||||
include_once("{$CFG->dirroot}/{$path}/lib.php");
|
||||
|
||||
// get the techproject object and all related stuff
|
||||
$techproject = get_record('techproject', 'id', $this_id);
|
||||
$course = get_record('course', 'id', $techproject->course);
|
||||
$module_context = get_record('context', 'id', $context_id);
|
||||
$cm = get_record('course_modules', 'id', $module_context->instance);
|
||||
if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', $module_context)) return false;
|
||||
|
||||
//group consistency check : checks the following situations about groups
|
||||
// if user is guest check access capabilities for guests :
|
||||
// guests can see default project, and other records if groups are liberal
|
||||
// TODO : change guestsallowed in a capability
|
||||
if (isguest() && $techproject->guestsallowed){
|
||||
if ($group_id && groupmode($course, $cm) == SEPARATEGROUPS)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// trap if user is not same group and groups are separated
|
||||
$current_group = get_current_group($course->id);
|
||||
if ((groupmode($course) == SEPARATEGROUPS) && $group_id != $current_group && $group_id) return false;
|
||||
|
||||
//trap if ungroupedsees is off in strict access mode and user is not teacher
|
||||
if ((groupmode($course) == SEPARATEGROUPS) && !$techproject->ungroupedsees && !$group_id && isteacher($user->id)) return false;
|
||||
|
||||
return true;
|
||||
} //techproject_check_text_access
|
||||
|
||||
?>
|
|
@ -1,158 +1,246 @@
|
|||
<?php
|
||||
/* Wiki Search Document class and functions
|
||||
* This file contains the mapping between a wiki page and it's indexable counterpart,
|
||||
* e.g. searchdocument->title = wikipage->pagename
|
||||
*
|
||||
* Functions for iterating and retrieving the necessary records are now also included
|
||||
* in this file, rather than mod/wiki/lib.php
|
||||
* */
|
||||
/**
|
||||
* Global Search Engine for Moodle
|
||||
* Michael Champanis (mchampan) [cynnical@gmail.com]
|
||||
* review 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||
* 2007/08/02
|
||||
*
|
||||
* document handling for wiki activity module
|
||||
* This file contains the mapping between a wiki page and it's indexable counterpart,
|
||||
* e.g. searchdocument->title = wikipage->pagename
|
||||
*
|
||||
* Functions for iterating and retrieving the necessary records are now also included
|
||||
* in this file, rather than mod/wiki/lib.php
|
||||
**/
|
||||
|
||||
require_once("$CFG->dirroot/search/documents/document.php");
|
||||
require_once("$CFG->dirroot/mod/wiki/lib.php");
|
||||
require_once("$CFG->dirroot/search/documents/document.php");
|
||||
require_once("$CFG->dirroot/mod/wiki/lib.php");
|
||||
|
||||
/* All the $doc->___ fields are required by the base document class!
|
||||
* Each and every module that requires search functionality must correctly
|
||||
* map their internal fields to the five $doc fields (id, title, author, contents
|
||||
* and url). Any module specific data can be added to the $data object, which is
|
||||
* serialised into a binary field in the index.
|
||||
* */
|
||||
class WikiSearchDocument extends SearchDocument {
|
||||
public function __construct(&$page, $wiki_id, $course_id, $group_id) {
|
||||
// generic information; required
|
||||
$doc->docid = $page->id;
|
||||
$doc->title = $page->pagename;
|
||||
$doc->date = $page->timemodified;
|
||||
/*
|
||||
* All the $doc->___ fields are required by the base document class!
|
||||
* Each and every module that requires search functionality must correctly
|
||||
* map their internal fields to the five $doc fields (id, title, author, contents
|
||||
* and url). Any module specific data can be added to the $data object, which is
|
||||
* serialised into a binary field in the index.
|
||||
**/
|
||||
class WikiSearchDocument extends SearchDocument {
|
||||
public function __construct(&$page, $wiki_id, $course_id, $group_id, $user_id, $context_id) {
|
||||
// generic information; required
|
||||
$doc->docid = $page['id'];
|
||||
$doc->documenttype = SEARCH_TYPE_WIKI;
|
||||
$doc->itemtype = 'standard';
|
||||
$doc->contextid = $context_id;
|
||||
|
||||
//remove '(ip.ip.ip.ip)' from wiki author field
|
||||
$doc->author = preg_replace('/\(.*?\)/', '', $page->author);
|
||||
$doc->contents = $page->content;
|
||||
$doc->url = wiki_make_link($wiki_id, $page->pagename, $page->version);
|
||||
|
||||
// module specific information; optional
|
||||
$data->version = $page->version;
|
||||
$data->wiki = $wiki_id;
|
||||
|
||||
// construct the parent class
|
||||
parent::__construct($doc, $data, SEARCH_TYPE_WIKI, $course_id, $group_id);
|
||||
$doc->title = $page['pagename'];
|
||||
$doc->date = $page['timemodified'];
|
||||
//remove '(ip.ip.ip.ip)' from wiki author field
|
||||
$doc->author = preg_replace('/\(.*?\)/', '', $page['author']);
|
||||
$doc->contents = $page['content'];
|
||||
$doc->url = wiki_make_link($wiki_id, $page['pagename'], $page['version']);
|
||||
|
||||
// module specific information; optional
|
||||
$data->version = $page['version'];
|
||||
$data->wiki = $wiki_id;
|
||||
|
||||
// construct the parent class
|
||||
parent::__construct($doc, $data, $course_id, $group_id, $user_id, PATH_FOR_SEARCH_TYPE_WIKI);
|
||||
} //constructor
|
||||
} //WikiSearchDocument
|
||||
} //WikiSearchDocument
|
||||
|
||||
function wiki_name_convert($str) {
|
||||
/**
|
||||
* converts a page name to cope Wiki constraints. Transforms spaces in plus.
|
||||
* @param str the name to convert
|
||||
* @return the converted name
|
||||
*/
|
||||
function wiki_name_convert($str) {
|
||||
return str_replace(' ', '+', $str);
|
||||
} //wiki_name_convert
|
||||
} //wiki_name_convert
|
||||
|
||||
function wiki_make_link($wiki_id, $title, $version) {
|
||||
/**
|
||||
* constructs a valid link to a wiki content
|
||||
* @param wikiId
|
||||
* @param title
|
||||
* @param version
|
||||
*/
|
||||
function wiki_make_link($wikiId, $title, $version) {
|
||||
global $CFG;
|
||||
return $CFG->wwwroot.'/mod/wiki/view.php?wid='.$wiki_id.'&page='.wiki_name_convert($title).'&version='.$version;
|
||||
} //wiki_make_link
|
||||
|
||||
//rescued and converted from ewikimoodlelib.php
|
||||
//retrieves latest version of a page
|
||||
function wiki_get_latest_page(&$entry, $pagename, $version=0) {
|
||||
return $CFG->wwwroot.'/mod/wiki/view.php?wid='.$wikiId.'&page='.wiki_name_convert($title).'&version='.$version;
|
||||
} //wiki_make_link
|
||||
|
||||
/**
|
||||
* rescued and converted from ewikimoodlelib.php
|
||||
* retrieves latest version of a page
|
||||
* @param entry the wiki object as a reference
|
||||
* @param pagename the name of the page known by the wiki engine
|
||||
* @param version
|
||||
*/
|
||||
function wiki_get_latest_page(&$entry, $pagename, $version = 0) {
|
||||
$pagename = "'".addslashes($pagename)."'";
|
||||
|
||||
|
||||
if ($version > 0 and is_int($version)) {
|
||||
$version = "AND (version=$version)";
|
||||
$version = "AND (version=$version)";
|
||||
} else {
|
||||
$version = '';
|
||||
} //else
|
||||
|
||||
$version = '';
|
||||
}
|
||||
|
||||
$select = "(pagename=$pagename) AND wiki=".$entry->id." $version ";
|
||||
$sort = 'version DESC';
|
||||
|
||||
|
||||
//change this to recordset_select, as per http://docs.moodle.org/en/Datalib_Notes
|
||||
if ($result_arr = get_records_select('wiki_pages', $select, $sort, '*', 0, 1)) {
|
||||
foreach ($result_arr as $obj) {
|
||||
$result_obj = $obj;
|
||||
} //foreach
|
||||
} //if
|
||||
|
||||
foreach ($result_arr as $obj) {
|
||||
$result_obj = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($result_obj)) {
|
||||
$result_obj->meta = @unserialize($result_obj->meta);
|
||||
return $result_obj;
|
||||
$result_obj->meta = @unserialize($result_obj->meta);
|
||||
return $result_obj;
|
||||
} else {
|
||||
return false;
|
||||
} //else
|
||||
} //wiki_get_latest_page
|
||||
return false;
|
||||
}
|
||||
} //wiki_get_latest_page
|
||||
|
||||
//fetches all pages, including old versions
|
||||
function wiki_get_pages(&$entry) {
|
||||
/**
|
||||
* fetches all pages, including old versions
|
||||
* @param entry the wiki object as a reference
|
||||
* @return an array of record objects that represents pages of this wiki object
|
||||
*/
|
||||
function wiki_get_pages(&$entry) {
|
||||
return get_records('wiki_pages', 'wiki', $entry->id);
|
||||
} //wiki_get_pages
|
||||
} //wiki_get_pages
|
||||
|
||||
//fetches all the latest versions of all the pages
|
||||
function wiki_get_latest_pages(&$entry) {
|
||||
//== (My)SQL for this
|
||||
/* select * from wiki_pages
|
||||
inner join
|
||||
(select wiki_pages.pagename, max(wiki_pages.version) as ver
|
||||
from wiki_pages group by pagename) as a
|
||||
on ((wiki_pages.version = a.ver) and
|
||||
(wiki_pages.pagename like a.pagename)) */
|
||||
/**
|
||||
* fetches all the latest versions of all the pages
|
||||
*
|
||||
*/
|
||||
function wiki_get_latest_pages(&$entry) {
|
||||
//== (My)SQL for this
|
||||
/* select * from wiki_pages
|
||||
inner join
|
||||
(select wiki_pages.pagename, max(wiki_pages.version) as ver
|
||||
from wiki_pages group by pagename) as a
|
||||
on ((wiki_pages.version = a.ver) and
|
||||
(wiki_pages.pagename like a.pagename)) */
|
||||
|
||||
$pages = array();
|
||||
|
||||
|
||||
//http://moodle.org/bugs/bug.php?op=show&bugid=5877&pos=0
|
||||
//if ($ids = get_records('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) {
|
||||
if ($rs = get_recordset('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) {
|
||||
$ids = $rs->GetRows();
|
||||
//--
|
||||
foreach ($ids as $id) {
|
||||
$pages[] = wiki_get_latest_page($entry, $id[0]);
|
||||
} //foreach
|
||||
} else {
|
||||
return false;
|
||||
} //else
|
||||
|
||||
if ($ids = get_records('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) {
|
||||
if ($pagesets = get_records('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) {
|
||||
foreach ($pagesets as $aPageset) {
|
||||
$pages[] = wiki_get_latest_page($entry, $aPageset->id);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return $pages;
|
||||
} //wiki_get_latest_pages
|
||||
} //wiki_get_latest_pages
|
||||
|
||||
function wiki_iterator() {
|
||||
return get_all_instances_in_courses("wiki", get_courses());
|
||||
} //wiki_iterator
|
||||
/**
|
||||
* part of search engine API
|
||||
*
|
||||
*/
|
||||
function wiki_iterator() {
|
||||
$wikis = get_records('wiki');
|
||||
return $wikis;
|
||||
} //wiki_iterator
|
||||
|
||||
/**
|
||||
* part of search engine API
|
||||
* @param wiki a wiki instance
|
||||
* @return an array of searchable deocuments
|
||||
*/
|
||||
function wiki_get_content_for_index(&$wiki) {
|
||||
|
||||
function wiki_get_content_for_index(&$wiki) {
|
||||
$documents = array();
|
||||
|
||||
$entries = wiki_get_entries($wiki);
|
||||
foreach($entries as $entry) {
|
||||
//all pages
|
||||
//$pages = wiki_get_pages($entry);
|
||||
|
||||
//latest pages
|
||||
$pages = wiki_get_latest_pages($entry);
|
||||
|
||||
if (is_array($pages)) {
|
||||
foreach($pages as $page) {
|
||||
if (strlen($page->content) > 0) {
|
||||
$documents[] = new WikiSearchDocument($page, $entry->wikiid, $entry->course, $entry->groupid);
|
||||
} //if
|
||||
} //foreach
|
||||
} //if
|
||||
} //foreach
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'wiki');
|
||||
$cm = get_record('course_modules', 'course', $entry->course, 'module', $coursemodule, 'instance', $entry->wikiid);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
|
||||
//all pages
|
||||
//$pages = wiki_get_pages($entry);
|
||||
|
||||
//latest pages
|
||||
$pages = wiki_get_latest_pages($entry);
|
||||
if (is_array($pages)) {
|
||||
foreach($pages as $page) {
|
||||
if (strlen($page->content) > 0) {
|
||||
$documents[] = new WikiSearchDocument(get_object_vars($page), $entry->wikiid, $entry->course, $entry->groupid, $page->userid, $context->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $documents;
|
||||
} //wiki_get_content_for_index
|
||||
} //wiki_get_content_for_index
|
||||
|
||||
//returns a single wiki search document based on a wiki_entry id
|
||||
function wiki_single_document($id) {
|
||||
$pages = get_recordset('wiki_pages', 'id', $id);
|
||||
$page = $pages->fields;
|
||||
/**
|
||||
* returns a single wiki search document based on a wiki_entry id
|
||||
* @param id the id of the wiki
|
||||
* @param itemtype the type of information (standard)
|
||||
* @retuen a searchable document
|
||||
*/
|
||||
function wiki_single_document($id, $itemtype) {
|
||||
$page = get_record('wiki_pages', 'id', $id);
|
||||
$entry = get_record('wiki_entries', 'id', $page->wiki);
|
||||
$coursemodule = get_field('modules', 'id', 'name', 'wiki');
|
||||
$cm = get_record('course_modules', 'course', $entry->course, 'module', $coursemodule, 'instance', $entry->wikiid);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
return new WikiSearchDocument(get_object_vars($page), $entry->wikiid, $entry->course, $entry->groupid, $page->userid, $context->id);
|
||||
} //wiki_single_document
|
||||
|
||||
$entries = get_recordset('wiki_entries', 'id', $page['wiki']);
|
||||
$entry = $entries->fields;
|
||||
/**
|
||||
* dummy delete function that packs id with itemtype.
|
||||
* this was here for a reason, but I can't remember it at the moment.
|
||||
*
|
||||
*/
|
||||
function wiki_delete($info, $itemtype) {
|
||||
$object->id = $info;
|
||||
$object->itemtype = $itemtype;
|
||||
return $object;
|
||||
} //wiki_delete
|
||||
|
||||
return new WikiSearchDocument($page, $entry['wikiid'], $entry['course'], $entry['groupid']);
|
||||
} //wiki_single_document
|
||||
|
||||
function wiki_delete($info) {
|
||||
return $info;
|
||||
} //wiki_delete
|
||||
|
||||
//returns the var names needed to build a sql query for addition/deletions
|
||||
function wiki_db_names() {
|
||||
//returns the var names needed to build a sql query for addition/deletions
|
||||
function wiki_db_names() {
|
||||
//[primary id], [table name], [time created field name], [time modified field name]
|
||||
return array('id', 'wiki_pages', 'created', 'lastmodified');
|
||||
} //wiki_db_names
|
||||
return array(array('id', 'wiki_pages', 'created', 'lastmodified', 'standard'));
|
||||
} //wiki_db_names
|
||||
|
||||
/**
|
||||
* this function handles the access policy to contents indexed as searchable documents. If this
|
||||
* function does not exist, the search engine assumes access is allowed.
|
||||
* When this point is reached, we already know that :
|
||||
* - user is legitimate in the surrounding context
|
||||
* - user may be guest and guest access is allowed to the module
|
||||
* - the function may perform local checks within the module information logic
|
||||
* @param path the access path to the module script code
|
||||
* @param itemtype the information subclassing (usefull for complex modules, defaults to 'standard')
|
||||
* @param this_id the item id within the information class denoted by itemtype. In wikies, this id
|
||||
* points out the indexed wiki page.
|
||||
* @param user the user record denoting the user who searches
|
||||
* @param group_id the current group used by the user when searching
|
||||
* @return true if access is allowed, false elsewhere
|
||||
*/
|
||||
function wiki_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
|
||||
global $CFG;
|
||||
|
||||
// get the wiki object and all related stuff
|
||||
$page = get_record('wiki_pages', 'id', $id);
|
||||
$entry = get_record('wiki_entries', 'id', $page->wiki);
|
||||
$course = get_record('course', 'id', $entry->course);
|
||||
$module_context = get_record('context', 'id', $context_id);
|
||||
$cm = get_record('course_modules', 'id', $module_context->instance);
|
||||
if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', $module_context)) return false;
|
||||
|
||||
//group consistency check : checks the following situations about groups
|
||||
// trap if user is not same group and groups are separated
|
||||
$current_group = get_current_group($course->id);
|
||||
if ((groupmode($course) == SEPARATEGROUPS) && $group_id != $current_group && !has_capability('moodle/site:accessallgroups', $module_context)) return false;
|
||||
|
||||
return true;
|
||||
} //wiki_check_text_access
|
||||
?>
|
Loading…
Add table
Add a link
Reference in a new issue