mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 08:26:37 +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
89
search/LISEZMOI.txt
Normal file
89
search/LISEZMOI.txt
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
Cette distribution partielle contient une refonte du moteur de
|
||||||
|
recherche globalde Moodle.
|
||||||
|
|
||||||
|
Le moteur de recherche est capable d'indexer et de rechercher
|
||||||
|
des informations dans un grand nombre de contenus stockés
|
||||||
|
dans la plate-forme à travers la manipulation des activités et
|
||||||
|
des blocs.
|
||||||
|
|
||||||
|
Le moteur de recherche procède à une première indexation des
|
||||||
|
ressources disponibles par action de l'administrateur. Une fois
|
||||||
|
cette indexation effectuée, le moteur maintient régulièrement les
|
||||||
|
indexes, en ajoutant les nouvelles entrées et en nettoyant les
|
||||||
|
entrées obsolètes.
|
||||||
|
|
||||||
|
La recherche permet d'obtenir des références d'accès au contexte
|
||||||
|
qui diffuse cette information, au nom de l'utilisateur courant.
|
||||||
|
Le filtrage des résultats enlève de la liste des réponses toute
|
||||||
|
ressource que la situation de l'utilisateur empêcherait de voir
|
||||||
|
s'il y accédait dans son contexte habituel.
|
||||||
|
|
||||||
|
Mise en oeuvre
|
||||||
|
##############
|
||||||
|
|
||||||
|
Pour déployer le moteur :
|
||||||
|
|
||||||
|
|
||||||
|
* Copie de fichiers
|
||||||
|
|
||||||
|
1. Ajouter les deux librairies fournies aux librairies de Moodle
|
||||||
|
2. Ecraser le répertoire "search" par le répertoire fourni
|
||||||
|
3. Ecraser le bloc "blocs/search" par le bloc fourni.
|
||||||
|
|
||||||
|
* Installation logique
|
||||||
|
|
||||||
|
4. Aller dans les notifications administratives et dérouler la procédure d'installation/mise à jour du bloc. L'installation crée la table image
|
||||||
|
des documents indexés et utilisés dans le module search.
|
||||||
|
|
||||||
|
5. Insérer un nouveau bloc de recherche globale dans la plate-forme
|
||||||
|
|
||||||
|
6. Effectuer une recherche vide (en administrateur)
|
||||||
|
|
||||||
|
7. Aller sur la page des statistiques
|
||||||
|
|
||||||
|
8. Activer l'indexation (indexsplash.php). Attention, si la plate-form contient beaucoup de contenus cette indexation peut être TRES LONGUE.
|
||||||
|
|
||||||
|
Pour effectuer des recherches, une fois la première indexation terminée, retourner au bloc de recherche et tenter une recherche.
|
||||||
|
|
||||||
|
Eléments pris en charge
|
||||||
|
#######################
|
||||||
|
|
||||||
|
Dans l'état actuel, les éléments indexés par le moteur sont :
|
||||||
|
|
||||||
|
- les entrées de forum
|
||||||
|
- les fiches de base de données
|
||||||
|
- les commentaires sur fiches de données
|
||||||
|
- les entrées de glossaire
|
||||||
|
- les commentaires sur entrées de glossaire
|
||||||
|
- les ressources natives Moodle
|
||||||
|
- les ressources physiques de type MSWord
|
||||||
|
- les ressources physiques de type PDF
|
||||||
|
- les ressources physiques de type fichier texte (.txt)
|
||||||
|
- les ressources physiques de type HTML (.htm et .html)
|
||||||
|
- les ressources physiques de type XML (.xml)
|
||||||
|
- les ressources physiques de type (Microsoft) Powerpoint (.ppt)
|
||||||
|
- les pages de wiki
|
||||||
|
- les entités de projet technique
|
||||||
|
- les sessions de chat
|
||||||
|
|
||||||
|
Extensions
|
||||||
|
##########
|
||||||
|
|
||||||
|
L'API du moteur de recherche permet désormais :
|
||||||
|
|
||||||
|
- l'indexation de contenus de blocs.
|
||||||
|
- l'indexation de modules contenant une information complexe ou de plusieurs types distincts
|
||||||
|
- la sécurisation des informations indexées lors des extractions de résultats
|
||||||
|
- l'indexation de tout module tiers par ajout d'un fichier php calibré
|
||||||
|
- l'indexation de toute nouvelle resource physique par ajout d'un fichier php calibré
|
||||||
|
|
||||||
|
Extensions futures
|
||||||
|
##################
|
||||||
|
|
||||||
|
- De nouvelles prises en charge de contenus tels que les attachements des forums, les attachement des glossaires, ainsi que d'autres modules non encore
|
||||||
|
implémentés.
|
||||||
|
|
||||||
|
- l'extension mnet de la recherche dans un réseau de moodle interconnectés.
|
||||||
|
|
||||||
|
|
||||||
|
|
90
search/READMETOO.txt
Normal file
90
search/READMETOO.txt
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
This partial distribution contains a complete review of the
|
||||||
|
Global Search Engine of Moodle.
|
||||||
|
|
||||||
|
The Global Search Engine stores indexes about a huge quantity
|
||||||
|
of information from within modules, block or resources stored
|
||||||
|
by Moodle either in the database or the file system.
|
||||||
|
|
||||||
|
The administrator initialy indexes the existing content. Once this
|
||||||
|
first initialization performed, the search engine maintains indexes
|
||||||
|
regularily, adding new entries, deleting obsolete one or updating
|
||||||
|
some that have changed.
|
||||||
|
|
||||||
|
Search will produce links for acceding the information in a similar
|
||||||
|
context as usually accessed, from the current user point of view.
|
||||||
|
Results filtering removes from results any link to information the
|
||||||
|
current user would not be allowed to acces on a straight situation.
|
||||||
|
|
||||||
|
Deployement
|
||||||
|
###########
|
||||||
|
|
||||||
|
For setting the engine :
|
||||||
|
|
||||||
|
|
||||||
|
* File copy
|
||||||
|
|
||||||
|
1. Add to Moodle's library both additional libraries provided in the distribution
|
||||||
|
2. Replace the "search" directory with the new one
|
||||||
|
3. Replace the "blocks/search" with the new one.
|
||||||
|
|
||||||
|
* Logical install
|
||||||
|
|
||||||
|
4. Browse to the administrative notification screen and let the
|
||||||
|
install/update process run. The install process creates the Moodle
|
||||||
|
table needed for backing the indexed documents identities.
|
||||||
|
|
||||||
|
5. Go to the block administration panel and setup once the Global Search
|
||||||
|
block. This will initialize useful parameters for the global search engine.
|
||||||
|
|
||||||
|
6. Insert a new Global Search block somewhere in a course or top-level screen.
|
||||||
|
|
||||||
|
7. Launch an empty search (you must be administrator).
|
||||||
|
|
||||||
|
8. Go to the statistics screen.
|
||||||
|
|
||||||
|
9. Activate indexation (indexersplash.php). Beware, if your Moodle has
|
||||||
|
a large amount of content, indexing process may be VERY LONG.
|
||||||
|
|
||||||
|
To search, go back to the search block and try a query.
|
||||||
|
|
||||||
|
Handled information for indexing
|
||||||
|
################################
|
||||||
|
|
||||||
|
In the actual state, the engine indexes the following information:
|
||||||
|
|
||||||
|
- forum posts
|
||||||
|
- database records (using textual fields only)
|
||||||
|
- database comments
|
||||||
|
- glossary entries
|
||||||
|
- glossary comments on entries
|
||||||
|
- Moodle native resources
|
||||||
|
- physical MSWord files as resources (.doc)
|
||||||
|
- physical Powerpoint files as resources (.ppt)
|
||||||
|
- physical PDF files as resources
|
||||||
|
- physical text files as resources (.txt)
|
||||||
|
- physical html files as resources (.htm and .html)
|
||||||
|
- physical xml files as resources (.xml)
|
||||||
|
- wiki pages
|
||||||
|
- techproject descriptions
|
||||||
|
- char sessions
|
||||||
|
|
||||||
|
Extensions
|
||||||
|
##########
|
||||||
|
|
||||||
|
The reviewed search engine API allows:
|
||||||
|
|
||||||
|
- indexing of blocks contents
|
||||||
|
- indexation of modules or blocks containing a complex information model
|
||||||
|
- securing the access to the results
|
||||||
|
- adding indexing handling adding a php calibrated script
|
||||||
|
- adding physical filetype handling adding a php calibrated script
|
||||||
|
|
||||||
|
Future extensions
|
||||||
|
#################
|
||||||
|
|
||||||
|
- Should be added more information to index such as forum and glossary attachements, so will other standard module contents.
|
||||||
|
|
||||||
|
- extending the search capability to a mnet network information space.
|
||||||
|
|
||||||
|
|
||||||
|
|
145
search/add.php
145
search/add.php
|
@ -1,46 +1,59 @@
|
||||||
<?php
|
<?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
|
||||||
|
*
|
||||||
|
* Asynchronous adder for new indexable contents
|
||||||
|
*
|
||||||
|
* Major chages in this review is passing the xxxx_db_names return to
|
||||||
|
* multiple arity to handle multiple document types modules
|
||||||
|
*/
|
||||||
|
|
||||||
require_once('../config.php');
|
require_once('../config.php');
|
||||||
require_once("$CFG->dirroot/search/lib.php");
|
require_once("$CFG->dirroot/search/lib.php");
|
||||||
|
|
||||||
require_login();
|
require_login();
|
||||||
|
|
||||||
if (empty($CFG->enableglobalsearch)) {
|
if (empty($CFG->enableglobalsearch)) {
|
||||||
error('Global searching is not enabled.');
|
error(get_string('globalsearchdisabled', 'search'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isadmin()) {
|
if (!isadmin()) {
|
||||||
error("You need to be an admin user to use this page.", "$CFG->wwwroot/login/index.php");
|
error(get_string('beadmin', 'search'), "$CFG->wwwroot/login/index.php");
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//check for php5 (lib.php)
|
//check for php5 (lib.php)
|
||||||
if (!search_check_php5()) {
|
if (!search_check_php5()) {
|
||||||
$phpversion = phpversion();
|
$phpversion = phpversion();
|
||||||
mtrace("Sorry, global search requires PHP 5.0.0 or later (currently using version $phpversion)");
|
mtrace("Sorry, global search requires PHP 5.0.0 or later (currently using version $phpversion)");
|
||||||
exit(0);
|
exit(0);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
require_once("$CFG->dirroot/search/indexlib.php");
|
require_once("$CFG->dirroot/search/indexlib.php");
|
||||||
|
|
||||||
$index = new Zend_Search_Lucene(SEARCH_INDEX_PATH);
|
$index = new Zend_Search_Lucene(SEARCH_INDEX_PATH);
|
||||||
$dbcontrol = new IndexDBControl();
|
$dbcontrol = new IndexDBControl();
|
||||||
$addition_count = 0;
|
$addition_count = 0;
|
||||||
|
$startindextime = time();
|
||||||
|
|
||||||
$indexdate = $CFG->search_indexer_run_date;
|
$indexdate = $CFG->search_indexer_run_date;
|
||||||
|
|
||||||
mtrace('<pre>Starting index update (additions)...');
|
mtrace('<pre>Starting index update (additions)...');
|
||||||
mtrace('Index size before: '.$CFG->search_index_size."\n");
|
mtrace('Index size before: '.$CFG->search_index_size."\n");
|
||||||
|
|
||||||
//get all modules
|
//get all modules
|
||||||
if ($mods = get_records_select('modules')) {
|
if ($mods = get_records_select('modules')) {
|
||||||
//append virtual modules onto array
|
|
||||||
$mods = array_merge($mods, search_get_additional_modules());
|
|
||||||
|
|
||||||
|
//append virtual modules onto array
|
||||||
|
$mods = array_merge($mods, search_get_additional_modules());
|
||||||
foreach ($mods as $mod) {
|
foreach ($mods as $mod) {
|
||||||
//build include file and function names
|
//build include file and function names
|
||||||
$class_file = $CFG->dirroot.'/search/documents/'.$mod->name.'_document.php';
|
$class_file = $CFG->dirroot.'/search/documents/'.$mod->name.'_document.php';
|
||||||
$db_names_function = $mod->name.'_db_names';
|
$db_names_function = $mod->name.'_db_names';
|
||||||
$get_document_function = $mod->name.'_single_document';
|
$get_document_function = $mod->name.'_single_document';
|
||||||
|
$get_newrecords_function = $mod->name.'_new_records';
|
||||||
$additions = array();
|
$additions = array();
|
||||||
|
|
||||||
if (file_exists($class_file)) {
|
if (file_exists($class_file)) {
|
||||||
|
@ -49,55 +62,83 @@
|
||||||
//if both required functions exist
|
//if both required functions exist
|
||||||
if (function_exists($db_names_function) and function_exists($get_document_function)) {
|
if (function_exists($db_names_function) and function_exists($get_document_function)) {
|
||||||
mtrace("Checking $mod->name module for additions.");
|
mtrace("Checking $mod->name module for additions.");
|
||||||
$values = $db_names_function();
|
$valuesArray = $db_names_function();
|
||||||
$where = (isset($values[4])) ? $values[4] : '';
|
if ($valuesArray){
|
||||||
|
foreach($valuesArray as $values){
|
||||||
|
$where = (isset($values[5])) ? 'AND ('.$values[5].')' : '';
|
||||||
|
$itemtypes = ($values[4] != '*') ? " AND itemtype = '{$values[4]}' " : '' ;
|
||||||
|
|
||||||
//select records in MODULE table, but not in SEARCH_DATABASE_TABLE
|
//select records in MODULE table, but not in SEARCH_DATABASE_TABLE
|
||||||
$sql = "select id, ".$values[0]." as docid from ".$values[1].
|
$table = SEARCH_DATABASE_TABLE;
|
||||||
" where id not in".
|
$query = "
|
||||||
" (select docid from ".SEARCH_DATABASE_TABLE." where doctype like '$mod->name')".
|
SELECT
|
||||||
" and ".$values[2]." > $indexdate".
|
docid,
|
||||||
" $where";
|
itemtype
|
||||||
|
FROM
|
||||||
|
{$CFG->prefix}{$table}
|
||||||
|
WHERE
|
||||||
|
doctype = '{$mod->name}'
|
||||||
|
$itemtypes
|
||||||
|
";
|
||||||
|
$docIds = get_records_sql_menu($query);
|
||||||
|
$docIdList = ($docIds) ? implode("','", array_keys($docIds)) : '' ;
|
||||||
|
|
||||||
$records = get_records_sql($sql);
|
$query = "
|
||||||
|
SELECT id,
|
||||||
|
{$values[0]} as docid
|
||||||
|
FROM
|
||||||
|
{$CFG->prefix}{$values[1]}
|
||||||
|
WHERE
|
||||||
|
id NOT IN ('{$docIdList}') and
|
||||||
|
{$values[2]} > {$indexdate}
|
||||||
|
$where
|
||||||
|
";
|
||||||
|
$records = get_records_sql($query);
|
||||||
|
|
||||||
//foreach record, build a module specific search document using the get_document function
|
// foreach record, build a module specific search document using the get_document function
|
||||||
if (is_array($records)) {
|
if (is_array($records)) {
|
||||||
foreach($records as $record) {
|
foreach($records as $record) {
|
||||||
$additions[] = $get_document_function($record->id);
|
$add = $get_document_function($record->docid, $values[4]);
|
||||||
} //foreach
|
// some documents may not be indexable
|
||||||
} //if
|
if ($add)
|
||||||
|
$additions[] = $add;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//foreach document, add it to the index and database table
|
// foreach document, add it to the index and database table
|
||||||
foreach ($additions as $add) {
|
foreach ($additions as $add) {
|
||||||
++$addition_count;
|
++$addition_count;
|
||||||
|
|
||||||
//object to insert into db
|
// object to insert into db
|
||||||
$dbid = $dbcontrol->addDocument($add);
|
$dbid = $dbcontrol->addDocument($add);
|
||||||
|
|
||||||
//synchronise db with index
|
// synchronise db with index
|
||||||
$add->addField(Zend_Search_Lucene_Field::Keyword('dbid', $dbid));
|
$add->addField(Zend_Search_Lucene_Field::Keyword('dbid', $dbid));
|
||||||
|
|
||||||
mtrace(" Add: $add->title (database id = $add->dbid, moodle instance id = $add->docid)");
|
mtrace(" Add: $add->title (database id = $add->dbid, moodle instance id = $add->docid)");
|
||||||
|
|
||||||
$index->addDocument($add);
|
$index->addDocument($add);
|
||||||
} //foreach
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
mtrace("No types to add.\n");
|
||||||
|
}
|
||||||
mtrace("Finished $mod->name.\n");
|
mtrace("Finished $mod->name.\n");
|
||||||
} //if
|
}
|
||||||
} //if
|
}
|
||||||
} //foreach
|
}
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//commit changes
|
// commit changes
|
||||||
$index->commit();
|
$index->commit();
|
||||||
|
|
||||||
//update index date and size
|
// update index date and size
|
||||||
set_config("search_indexer_run_date", time());
|
set_config("search_indexer_run_date", $startindextime);
|
||||||
set_config("search_index_size", (int)$CFG->search_index_size + (int)$addition_count);
|
set_config("search_index_size", (int)$CFG->search_index_size + (int)$addition_count);
|
||||||
|
|
||||||
//print some additional info
|
// print some additional info
|
||||||
mtrace("Added $addition_count documents.");
|
mtrace("Added $addition_count documents.");
|
||||||
mtrace('Index size after: '.$index->count().'</pre>');
|
mtrace('Index size after: '.$index->count().'</pre>');
|
||||||
|
|
||||||
?>
|
?>
|
|
@ -1,35 +1,47 @@
|
||||||
<?php
|
<?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
|
||||||
|
*
|
||||||
|
* Asynchronous index cleaner
|
||||||
|
*
|
||||||
|
* Major chages in this review is passing the xxxx_db_names return to
|
||||||
|
* multiple arity to handle multiple document types modules
|
||||||
|
*/
|
||||||
|
|
||||||
require_once('../config.php');
|
require_once('../config.php');
|
||||||
require_once("$CFG->dirroot/search/lib.php");
|
require_once("$CFG->dirroot/search/lib.php");
|
||||||
|
|
||||||
require_login();
|
require_login();
|
||||||
|
|
||||||
if (empty($CFG->enableglobalsearch)) {
|
if (empty($CFG->enableglobalsearch)) {
|
||||||
error('Global searching is not enabled.');
|
error(get_string('globalsearchdisabled', 'search'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isadmin()) {
|
if (!isadmin()) {
|
||||||
error("You need to be an admin user to use this page.", "$CFG->wwwroot/login/index.php");
|
error(get_string('beadmin', 'search'), "$CFG->wwwroot/login/index.php");
|
||||||
} //if
|
} //if
|
||||||
|
|
||||||
//check for php5 (lib.php)
|
//check for php5 (lib.php)
|
||||||
if (!search_check_php5()) {
|
if (!search_check_php5()) {
|
||||||
$phpversion = phpversion();
|
$phpversion = phpversion();
|
||||||
mtrace("Sorry, global search requires PHP 5.0.0 or later (currently using version $phpversion)");
|
mtrace("Sorry, global search requires PHP 5.0.0 or later (currently using version $phpversion)");
|
||||||
exit(0);
|
exit(0);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
require_once("$CFG->dirroot/search/indexlib.php");
|
require_once("$CFG->dirroot/search/indexlib.php");
|
||||||
|
|
||||||
$index = new Zend_Search_Lucene(SEARCH_INDEX_PATH);
|
$index = new Zend_Search_Lucene(SEARCH_INDEX_PATH);
|
||||||
$dbcontrol = new IndexDBControl();
|
$dbcontrol = new IndexDBControl();
|
||||||
$deletion_count = 0;
|
$deletion_count = 0;
|
||||||
|
$startcleantime = time();
|
||||||
|
|
||||||
mtrace('<pre>Starting clean-up of removed records...');
|
mtrace('<pre>Starting clean-up of removed records...');
|
||||||
mtrace('Index size before: '.$CFG->search_index_size."\n");
|
mtrace('Index size before: '.$CFG->search_index_size."\n");
|
||||||
|
|
||||||
if ($mods = get_records_select('modules')) {
|
if ($mods = get_records_select('modules')) {
|
||||||
$mods = array_merge($mods, search_get_additional_modules());
|
$mods = array_merge($mods, search_get_additional_modules());
|
||||||
|
|
||||||
foreach ($mods as $mod) {
|
foreach ($mods as $mod) {
|
||||||
|
@ -42,29 +54,52 @@
|
||||||
if (file_exists($class_file)) {
|
if (file_exists($class_file)) {
|
||||||
require_once($class_file);
|
require_once($class_file);
|
||||||
|
|
||||||
|
//if both required functions exist
|
||||||
if (function_exists($delete_function) and function_exists($db_names_function)) {
|
if (function_exists($delete_function) and function_exists($db_names_function)) {
|
||||||
mtrace("Checking $mod->name module for deletions.");
|
mtrace("Checking $mod->name module for deletions.");
|
||||||
$values = $db_names_function();
|
$valuesArray = $db_names_function();
|
||||||
|
if ($valuesArray){
|
||||||
|
foreach($valuesArray as $values){
|
||||||
|
$where = (isset($values[5])) ? 'WHERE '.$values[5] : '';
|
||||||
|
$itemtypes = ($values[4] != '*') ? " itemtype = '{$values[4]}' AND " : '' ;
|
||||||
|
$query = "
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
{$values[0]}
|
||||||
|
FROM
|
||||||
|
{$CFG->prefix}{$values[1]}
|
||||||
|
$where
|
||||||
|
";
|
||||||
|
$docIds = get_records_sql($query);
|
||||||
|
$docIdList = ($docIds) ? implode("','", array_keys($docIds)) : '' ;
|
||||||
|
|
||||||
$sql = "select id, docid from ".SEARCH_DATABASE_TABLE.
|
$table = SEARCH_DATABASE_TABLE;
|
||||||
" where doctype like '$mod->name'".
|
$query = "
|
||||||
" and docid not in".
|
SELECT
|
||||||
" (select ".$values[0]." from ".$values[1].")";
|
id,
|
||||||
|
docid
|
||||||
|
FROM
|
||||||
|
{$CFG->prefix}{$table}
|
||||||
|
WHERE
|
||||||
|
doctype = '{$mod->name}' AND
|
||||||
|
$itemtypes
|
||||||
|
docid not in ('{$docIdList}')
|
||||||
|
";
|
||||||
|
$records = get_records_sql($query);
|
||||||
|
|
||||||
$records = get_records_sql($sql);
|
// build an array of all the deleted records
|
||||||
|
|
||||||
//build an array of all the deleted records
|
|
||||||
if (is_array($records)) {
|
if (is_array($records)) {
|
||||||
foreach($records as $record) {
|
foreach($records as $record) {
|
||||||
$deletions[] = $delete_function($record->docid);
|
$deletions[] = $delete_function($record->docid, $values[4]);
|
||||||
} //foreach
|
}
|
||||||
} //if
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($deletions as $delete) {
|
foreach ($deletions as $delete) {
|
||||||
//find the specific document in the index, using it's docid and doctype as keys
|
// find the specific document in the index, using it's docid and doctype as keys
|
||||||
$doc = $index->find("+docid:$delete +doctype:$mod->name");
|
$doc = $index->find("+docid:{$delete->id} +doctype:$mod->name +itemtype:{$delete->itemtype}");
|
||||||
|
|
||||||
//get the record, should only be one
|
// get the record, should only be one
|
||||||
foreach ($doc as $thisdoc) {
|
foreach ($doc as $thisdoc) {
|
||||||
++$deletion_count;
|
++$deletion_count;
|
||||||
mtrace(" Delete: $thisdoc->title (database id = $thisdoc->dbid, index id = $thisdoc->id, moodle instance id = $thisdoc->docid)");
|
mtrace(" Delete: $thisdoc->title (database id = $thisdoc->dbid, index id = $thisdoc->id, moodle instance id = $thisdoc->docid)");
|
||||||
|
@ -72,23 +107,26 @@
|
||||||
//remove it from index and database table
|
//remove it from index and database table
|
||||||
$dbcontrol->delDocument($thisdoc);
|
$dbcontrol->delDocument($thisdoc);
|
||||||
$index->delete($thisdoc->id);
|
$index->delete($thisdoc->id);
|
||||||
} //foreach
|
}
|
||||||
} //foreach
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
mtrace("No types to delete.\n");
|
||||||
|
}
|
||||||
mtrace("Finished $mod->name.\n");
|
mtrace("Finished $mod->name.\n");
|
||||||
} //if
|
}
|
||||||
} //if
|
}
|
||||||
} //foreach
|
}
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//commit changes
|
//commit changes
|
||||||
$index->commit();
|
$index->commit();
|
||||||
|
|
||||||
//update index date and index size
|
//update index date and index size
|
||||||
set_config("search_indexer_run_date", time());
|
set_config("search_indexer_cleanup_date", $startcleantime);
|
||||||
set_config("search_index_size", (int)$CFG->search_index_size - (int)$deletion_count);
|
set_config("search_index_size", (int)$CFG->search_index_size - (int)$deletion_count);
|
||||||
|
|
||||||
mtrace("Finished $deletion_count removals.");
|
mtrace("Finished $deletion_count removals.");
|
||||||
mtrace('Index size after: '.$index->count().'</pre>');
|
mtrace('Index size after: '.$index->count().'</pre>');
|
||||||
|
|
||||||
?>
|
?>
|
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,11 +1,34 @@
|
||||||
<?php
|
<?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 {
|
abstract class SearchDocument extends Zend_Search_Lucene_Document {
|
||||||
public function __construct(&$doc, &$data, $document_type, $course_id, $group_id) {
|
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));
|
$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('title', $doc->title));
|
||||||
$this->addField(Zend_Search_Lucene_Field::Text('author', $doc->author));
|
$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::UnStored('contents', $doc->contents));
|
||||||
|
@ -15,10 +38,21 @@
|
||||||
//additional data added on a per-module basis
|
//additional data added on a per-module basis
|
||||||
$this->addField(Zend_Search_Lucene_Field::Binary('data', serialize($data)));
|
$this->addField(Zend_Search_Lucene_Field::Binary('data', serialize($data)));
|
||||||
|
|
||||||
$this->addField(Zend_Search_Lucene_Field::Keyword('doctype', $document_type));
|
// adding a path allows the document to know where to find specific library calls
|
||||||
$this->addField(Zend_Search_Lucene_Field::Keyword('course_id', $course_id));
|
// for checking access to a module or block content. The Lucene records should only
|
||||||
$this->addField(Zend_Search_Lucene_Field::Keyword('group_id', $group_id));
|
// 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
|
} //constructor
|
||||||
} //SearchDocument
|
} //SearchDocument
|
||||||
|
|
||||||
?>
|
?>
|
|
@ -1,105 +1,159 @@
|
||||||
<?php
|
<?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) {
|
* constructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct(&$post, $forum_id, $course_id, $itemtype, $context_id) {
|
||||||
// generic information
|
// generic information
|
||||||
$doc->docid = $post['id'];
|
$doc->docid = $post['id'];
|
||||||
|
$doc->documenttype = SEARCH_TYPE_FORUM;
|
||||||
|
$doc->itemtype = $itemtype;
|
||||||
|
$doc->contextid = $context_id;
|
||||||
|
|
||||||
$doc->title = $post['subject'];
|
$doc->title = $post['subject'];
|
||||||
$doc->author = $post['firstname']." ".$post['lastname'];
|
$doc->author = $post['firstname']." ".$post['lastname'];
|
||||||
$doc->contents = $post['message'];
|
$doc->contents = $post['message'];
|
||||||
$doc->date = $post['created'];
|
$doc->date = $post['created'];
|
||||||
|
|
||||||
$doc->url = forum_make_link($post['discussion'], $post['id']);
|
$doc->url = forum_make_link($post['discussion'], $post['id']);
|
||||||
|
|
||||||
// module specific information
|
// module specific information
|
||||||
$data->forum = $forum_id;
|
$data->forum = $forum_id;
|
||||||
$data->discussion = $post['discussion'];
|
$data->discussion = $post['discussion'];
|
||||||
|
|
||||||
parent::__construct($doc, $data, SEARCH_TYPE_FORUM, $course_id, $group_id);
|
parent::__construct($doc, $data, $course_id, $post['groupid'], $post['userid'], PATH_FOR_SEARCH_TYPE_FORUM);
|
||||||
} //constructor
|
} //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;
|
global $CFG;
|
||||||
|
|
||||||
return $CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion_id.'#'.$post_id;
|
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
|
* search standard API
|
||||||
return @get_all_instances_in_courses("forum", get_courses());
|
*
|
||||||
} //forum_iterator
|
*/
|
||||||
|
function forum_iterator() {
|
||||||
|
$forums = get_records('forum');
|
||||||
|
return $forums;
|
||||||
|
} //forum_iterator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* search standard API
|
||||||
|
* @param forum a forum instance
|
||||||
|
* @return an array of searchable documents
|
||||||
|
*/
|
||||||
|
function forum_get_content_for_index(&$forum) {
|
||||||
|
|
||||||
function forum_get_content_for_index(&$forum) {
|
|
||||||
$documents = array();
|
$documents = array();
|
||||||
if (!$forum) return $documents;
|
if (!$forum) return $documents;
|
||||||
|
|
||||||
$posts = forum_get_discussions_fast($forum->id);
|
$posts = forum_get_discussions_fast($forum->id);
|
||||||
if (!$posts) return $documents;
|
if (!$posts) return $documents;
|
||||||
|
|
||||||
while (!$posts->EOF) {
|
$coursemodule = get_field('modules', 'id', 'name', 'forum');
|
||||||
$post = $posts->fields;
|
$cm = get_record('course_modules', 'course', $forum->course, 'module', $coursemodule, 'instance', $forum->id);
|
||||||
|
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||||
if (is_array($post)) {
|
|
||||||
if (strlen($post['message']) > 0 && ($post['deleted'] != 1)) {
|
|
||||||
$documents[] = new ForumSearchDocument($post, $forum->id, $forum->course, $post['groupid']);
|
|
||||||
} //if
|
|
||||||
|
|
||||||
if ($children = forum_get_child_posts_fast($post['id'], $forum->id)) {
|
|
||||||
while (!$children->EOF) {
|
|
||||||
$child = $children->fields;
|
|
||||||
|
|
||||||
if (strlen($child['message']) > 0 && ($child['deleted'] != 1)) {
|
|
||||||
$documents[] = new ForumSearchDocument($child, $forum->id, $forum->course, $post['groupid']);
|
|
||||||
} //if
|
|
||||||
|
|
||||||
$children->MoveNext();
|
|
||||||
} //foreach
|
|
||||||
} //if
|
|
||||||
} //if
|
|
||||||
|
|
||||||
$posts->MoveNext();
|
|
||||||
} //foreach
|
|
||||||
|
|
||||||
|
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;
|
return $documents;
|
||||||
} //forum_get_content_for_index
|
} //forum_get_content_for_index
|
||||||
|
|
||||||
//returns a single forum search document based on a forum_entry id
|
/**
|
||||||
function forum_single_document($id) {
|
* returns a single forum search document based on a forum entry id
|
||||||
$posts = get_recordset('forum_posts', 'id', $id);
|
* @param id an id for a single information stub
|
||||||
$post = $posts->fields;
|
* @param itemtype the type of information
|
||||||
|
*/
|
||||||
|
function forum_single_document($id, $itemtype) {
|
||||||
|
|
||||||
$discussions = get_recordset('forum_discussions', 'id', $post['discussion']);
|
// both known item types are posts so get them the same way
|
||||||
$discussion = $discussions->fields;
|
$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
|
||||||
|
|
||||||
$forums = get_recordset('forum', 'id', $discussion['forum']);
|
/**
|
||||||
$forum = $forums->fields;
|
* 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 new ForumSearchDocument($post, $forum['id'], $forum['course'], $post['groupid']);
|
/**
|
||||||
} //forum_single_document
|
* returns the var names needed to build a sql query for addition/deletions
|
||||||
|
*
|
||||||
function forum_delete($info) {
|
*/
|
||||||
return $info;
|
function forum_db_names() {
|
||||||
} //forum_delete
|
|
||||||
|
|
||||||
//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]
|
//[primary id], [table name], [time created field name], [time modified field name]
|
||||||
return array('id', 'forum_posts', 'created', 'modified');
|
return array(
|
||||||
} //forum_db_names
|
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;
|
global $CFG, $USER;
|
||||||
|
|
||||||
$timelimit='';
|
$timelimit='';
|
||||||
|
|
||||||
if (!empty($CFG->forum_enabletimedposts)) {
|
if (!empty($CFG->forum_enabletimedposts)) {
|
||||||
if (!((isadmin() and !empty($CFG->admineditalways)) || isteacher(get_field('forum', 'course', 'id', $forum)))) {
|
if (!((isadmin() and !empty($CFG->admineditalways)) || isteacher(get_field('forum', 'course', 'id', $forum_id)))) {
|
||||||
$now = time();
|
$now = time();
|
||||||
$timelimit = " AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')";
|
$timelimit = " AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')";
|
||||||
if (!empty($USER->id)) {
|
if (!empty($USER->id)) {
|
||||||
|
@ -109,27 +163,107 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return get_recordset_sql("SELECT p.id, p.subject, p.discussion, p.message,
|
$query = "
|
||||||
p.deleted, d.groupid, u.firstname, u.lastname
|
SELECT
|
||||||
FROM {$CFG->prefix}forum_discussions d
|
p.id,
|
||||||
JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
|
p.subject,
|
||||||
JOIN {$CFG->prefix}user u ON p.userid = u.id
|
p.discussion,
|
||||||
WHERE d.forum = '$forum'
|
p.message,
|
||||||
AND p.parent = 0
|
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
|
$timelimit
|
||||||
ORDER BY d.timemodified DESC");
|
ORDER BY
|
||||||
} //forum_get_discussions_fast
|
d.timemodified DESC
|
||||||
|
";
|
||||||
|
return get_records_sql($query);
|
||||||
|
} //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;
|
global $CFG;
|
||||||
|
|
||||||
return get_recordset_sql("SELECT p.id, p.subject, p.discussion, p.message, p.deleted,
|
$query = "
|
||||||
$forumid AS forum, u.firstname, u.lastname
|
SELECT
|
||||||
FROM {$CFG->prefix}forum_posts p
|
p.id,
|
||||||
LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
|
p.subject,
|
||||||
WHERE p.parent = '$parent'
|
p.discussion,
|
||||||
ORDER BY p.created ASC");
|
p.message,
|
||||||
} //forum_get_child_posts_fast
|
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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
<?php
|
||||||
/* This document illustrates how easy it is to add a module to
|
/**
|
||||||
* the search index - the only modifications made were creating
|
* Global Search Engine for Moodle
|
||||||
* this file, and adding the SEARCH_TYPE_GLOSSARY constant to
|
* Michael Champanis (mchampan) [cynnical@gmail.com]
|
||||||
* search/lib.php - everything else is automatically handled
|
* review 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||||
* by the indexer script.
|
* 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) {
|
* a class for representing searchable information
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
class GlossarySearchDocument extends SearchDocument {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* document constructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct(&$entry, $course_id, $context_id) {
|
||||||
// generic information; required
|
// generic information; required
|
||||||
$doc->docid = $entry['id'];
|
$doc->docid = $entry['id'];
|
||||||
|
$doc->documenttype = SEARCH_TYPE_GLOSSARY;
|
||||||
|
$doc->itemtype = 'standard';
|
||||||
|
$doc->contextid = $context_id;
|
||||||
|
|
||||||
$doc->title = $entry['concept'];
|
$doc->title = $entry['concept'];
|
||||||
$doc->date = $entry['timecreated'];
|
$doc->date = $entry['timecreated'];
|
||||||
|
|
||||||
$user = get_recordset('user', 'id', $entry['userid'])->fields;
|
if ($entry['userid'])
|
||||||
|
$user = get_record('user', 'id', $entry['userid']);
|
||||||
$doc->author = $user['firstname'].' '.$user['lastname'];
|
$doc->author = ($user ) ? $user->firstname.' '.$user->lastname : '' ;
|
||||||
$doc->contents = $entry['definition'];
|
$doc->contents = strip_tags($entry['definition']);
|
||||||
$doc->url = glossary_make_link($entry['id']);
|
$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
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
// module specific information; optional
|
||||||
$data->glossary = $glossary_id;
|
$data->glossary = $glossary_id;
|
||||||
|
|
||||||
// construct the parent class
|
// construct the parent class
|
||||||
parent::__construct($doc, $data, SEARCH_TYPE_GLOSSARY, $course_id, $group_id);
|
parent::__construct($doc, $data, $course_id, -1, $entry['userid'], PATH_FOR_SEARCH_TYPE_GLOSSARY);
|
||||||
} //constructor
|
} //constructor
|
||||||
} //GlossarySearchDocument
|
} //GlossaryCommentSearchDocument
|
||||||
|
|
||||||
function glossary_make_link($entry_id) {
|
/**
|
||||||
|
* 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;
|
global $CFG;
|
||||||
|
|
||||||
//links directly to entry
|
//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]'
|
//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);";
|
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
|
} //glossary_make_link
|
||||||
|
|
||||||
function glossary_iterator() {
|
/**
|
||||||
return get_all_instances_in_courses("glossary", get_courses());
|
* part of search engine API
|
||||||
} //glossary_iterator
|
*
|
||||||
|
*/
|
||||||
|
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();
|
$documents = array();
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$entries = get_recordset('glossary_entries', 'glossaryid', $glossary->id);
|
// index comments
|
||||||
|
if (count($entryIds)){
|
||||||
while (!$entries->EOF) {
|
$entryIdList = implode(',', $entryIds);
|
||||||
$entry = $entries->fields;
|
$comments = get_records_list('glossary_comments', 'entryid', $entryIdList);
|
||||||
|
if ($comments){
|
||||||
if ($entry and strlen($entry['definition']) > 0) {
|
foreach($comments as $comment) {
|
||||||
$documents[] = new GlossarySearchDocument($entry, $glossary->id, $glossary->course, -1);
|
if (strlen($comment->entrycomment) > 0) {
|
||||||
} //if
|
$comment->concept = $concepts[$comment->entryid];
|
||||||
|
$documents[] = new GlossaryCommentSearchDocument(get_object_vars($comment), $glossary->id, $glossary->course, $context->id);
|
||||||
$entries->MoveNext();
|
}
|
||||||
} //foreach
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return $documents;
|
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) {
|
* part of search engine API
|
||||||
$entries = get_recordset('glossary_entries', 'id', $id);
|
* @param id the glossary entry identifier
|
||||||
$entry = $entries->fields;
|
* @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
|
* returns the var names needed to build a sql query for addition/deletions
|
||||||
|
*
|
||||||
//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_db_names() {
|
||||||
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() {
|
|
||||||
//[primary id], [table name], [time created field name], [time modified field name]
|
//[primary id], [table name], [time created field name], [time modified field name]
|
||||||
return array('id', 'glossary_entries', 'timecreated', 'timemodified');
|
return array(
|
||||||
} //glossary_db_names
|
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,14 +1,34 @@
|
||||||
<?php
|
<?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) {
|
* a class for representing searchable information
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
class ResourceSearchDocument extends SearchDocument {
|
||||||
|
public function __construct(&$resource, $context_id) {
|
||||||
// generic information; required
|
// generic information; required
|
||||||
$doc->docid = $resource['id'];
|
$doc->docid = $resource['trueid'];
|
||||||
|
$doc->documenttype = SEARCH_TYPE_RESOURCE;
|
||||||
|
$doc->itemtype = $resource['type'];
|
||||||
|
$doc->contextid = $context_id;
|
||||||
|
|
||||||
$doc->title = strip_tags($resource['name']);
|
$doc->title = strip_tags($resource['name']);
|
||||||
$doc->date = $resource['timemodified'];
|
$doc->date = $resource['timemodified'];
|
||||||
|
|
||||||
$doc->author = '';
|
$doc->author = '';
|
||||||
$doc->contents = strip_tags($resource['summary']).' '.strip_tags($resource['alltext']);
|
$doc->contents = strip_tags($resource['summary']).' '.strip_tags($resource['alltext']);
|
||||||
$doc->url = resource_make_link($resource['id']);
|
$doc->url = resource_make_link($resource['id']);
|
||||||
|
@ -17,70 +37,271 @@
|
||||||
$data = array();
|
$data = array();
|
||||||
|
|
||||||
// construct the parent class
|
// construct the parent class
|
||||||
parent::__construct($doc, $data, SEARCH_TYPE_RESOURCE, $resource['course'], -1);
|
parent::__construct($doc, $data, $resource['course'], 0, 0, PATH_FOR_SEARCH_TYPE_RESOURCE);
|
||||||
} //constructor
|
} //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;
|
global $CFG;
|
||||||
return $CFG->wwwroot.'/mod/resource/view.php?r='.$resource_id;
|
|
||||||
} //resource_make_link
|
|
||||||
|
|
||||||
function resource_iterator() {
|
return $CFG->wwwroot.'/mod/resource/view.php?id='.$resource_id;
|
||||||
|
} //resource_make_link
|
||||||
|
|
||||||
|
/**
|
||||||
|
* part of standard API
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function resource_iterator() {
|
||||||
//trick to leave search indexer functionality intact, but allow
|
//trick to leave search indexer functionality intact, but allow
|
||||||
//this document to only use the below function to return info
|
//this document to only use the below function to return info
|
||||||
//to be searched
|
//to be searched
|
||||||
return array(true);
|
return array(true);
|
||||||
} //resource_iterator
|
} //resource_iterator
|
||||||
|
|
||||||
//this function does not need a content iterator, returns all the info
|
/**
|
||||||
//itself; remember to fake the iterator array though
|
* part of standard API
|
||||||
function resource_get_content_for_index(&$notneeded) {
|
* 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();
|
$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 *
|
foreach($resources as $aResource){
|
||||||
FROM {$CFG->prefix}resource
|
$coursemodule = get_field('modules', 'id', 'name', 'resource');
|
||||||
WHERE alltext NOT LIKE ""
|
$cm = get_record('course_modules', 'course', $aResource->course, 'module', $coursemodule, 'instance', $aResource->id);
|
||||||
AND alltext NOT LIKE " "
|
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||||
AND alltext NOT LIKE " "
|
$aResource->id = $cm->id;
|
||||||
AND TYPE != "file"');
|
$documents[] = new ResourceSearchDocument(get_object_vars($aResource), $context->id);
|
||||||
|
mtrace("finished $aResource->name");
|
||||||
|
}
|
||||||
|
|
||||||
while (!$resources->EOF) {
|
// special physical files handling
|
||||||
$resource = $resources->fields;
|
/**
|
||||||
|
* this sequence searches for a compatible physical stream handler for getting a text
|
||||||
if ($resource) {
|
* equivalence for the content.
|
||||||
$documents[] = new ResourceSearchDocument($resource);
|
*
|
||||||
} //if
|
*/
|
||||||
|
if (@$CFG->block_search_enable_file_indexing){
|
||||||
$resources->MoveNext();
|
$query = "
|
||||||
} //foreach
|
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;
|
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) {
|
* get text from a physical file
|
||||||
$resources = get_recordset_sql('SELECT *
|
* @param resource a resource for which to fetch some representative text
|
||||||
FROM {$CFG->prefix}resource
|
* @param getsingle if true, returns a single search document, elsewhere return the array
|
||||||
WHERE alltext NOT LIKE ""
|
* given as documents increased by one
|
||||||
AND alltext NOT LIKE " "
|
* @param documents the array of documents, by ref, where to add the new document.
|
||||||
AND alltext NOT LIKE " "
|
* @return a search document when unique or false.
|
||||||
AND TYPE != "file",
|
*/
|
||||||
AND id = '.$id);
|
function resource_get_physical_file(&$resource, $context_id, $getsingle, &$documents = null){
|
||||||
|
global $CFG;
|
||||||
|
|
||||||
$resource = $resources->fields;
|
// cannot index empty references
|
||||||
|
if (empty($resource->reference)) return false;
|
||||||
|
|
||||||
return new ResourceSearchDocument($resource);
|
// cannot index remote resources
|
||||||
} //resource_single_document
|
if (resource_is_url($resource->reference)){
|
||||||
|
mtrace("Cannot index remote URLs.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function resource_delete($info) {
|
$fileparts = pathinfo($resource->reference);
|
||||||
return $info;
|
// cannot index unknown or masked types
|
||||||
} //resource_delete
|
if (empty($fileparts['extension'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//returns the var names needed to build a sql query for addition/deletions
|
// cannot index non existent file
|
||||||
function resource_db_names() {
|
$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']);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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]
|
//[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'");
|
return array(array('id', 'resource', 'timemodified', 'timemodified', '*', " (alltext != '' AND alltext != ' ' AND alltext != ' ' AND TYPE != 'file') OR TYPE = 'file' "));
|
||||||
} //resource_db_names
|
} //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,61 +1,88 @@
|
||||||
<?php
|
<?php
|
||||||
/* Wiki Search Document class and functions
|
/**
|
||||||
* This file contains the mapping between a wiki page and it's indexable counterpart,
|
* Global Search Engine for Moodle
|
||||||
* e.g. searchdocument->title = wikipage->pagename
|
* Michael Champanis (mchampan) [cynnical@gmail.com]
|
||||||
*
|
* review 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||||
* Functions for iterating and retrieving the necessary records are now also included
|
* 2007/08/02
|
||||||
* in this file, rather than mod/wiki/lib.php
|
*
|
||||||
* */
|
* 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/search/documents/document.php");
|
||||||
require_once("$CFG->dirroot/mod/wiki/lib.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
|
* All the $doc->___ fields are required by the base document class!
|
||||||
* map their internal fields to the five $doc fields (id, title, author, contents
|
* Each and every module that requires search functionality must correctly
|
||||||
* and url). Any module specific data can be added to the $data object, which is
|
* map their internal fields to the five $doc fields (id, title, author, contents
|
||||||
* serialised into a binary field in the index.
|
* 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) {
|
class WikiSearchDocument extends SearchDocument {
|
||||||
|
public function __construct(&$page, $wiki_id, $course_id, $group_id, $user_id, $context_id) {
|
||||||
// generic information; required
|
// generic information; required
|
||||||
$doc->docid = $page->id;
|
$doc->docid = $page['id'];
|
||||||
$doc->title = $page->pagename;
|
$doc->documenttype = SEARCH_TYPE_WIKI;
|
||||||
$doc->date = $page->timemodified;
|
$doc->itemtype = 'standard';
|
||||||
|
$doc->contextid = $context_id;
|
||||||
|
|
||||||
|
$doc->title = $page['pagename'];
|
||||||
|
$doc->date = $page['timemodified'];
|
||||||
//remove '(ip.ip.ip.ip)' from wiki author field
|
//remove '(ip.ip.ip.ip)' from wiki author field
|
||||||
$doc->author = preg_replace('/\(.*?\)/', '', $page->author);
|
$doc->author = preg_replace('/\(.*?\)/', '', $page['author']);
|
||||||
$doc->contents = $page->content;
|
$doc->contents = $page['content'];
|
||||||
$doc->url = wiki_make_link($wiki_id, $page->pagename, $page->version);
|
$doc->url = wiki_make_link($wiki_id, $page['pagename'], $page['version']);
|
||||||
|
|
||||||
// module specific information; optional
|
// module specific information; optional
|
||||||
$data->version = $page->version;
|
$data->version = $page['version'];
|
||||||
$data->wiki = $wiki_id;
|
$data->wiki = $wiki_id;
|
||||||
|
|
||||||
// construct the parent class
|
// construct the parent class
|
||||||
parent::__construct($doc, $data, SEARCH_TYPE_WIKI, $course_id, $group_id);
|
parent::__construct($doc, $data, $course_id, $group_id, $user_id, PATH_FOR_SEARCH_TYPE_WIKI);
|
||||||
} //constructor
|
} //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);
|
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;
|
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
|
return $CFG->wwwroot.'/mod/wiki/view.php?wid='.$wikiId.'&page='.wiki_name_convert($title).'&version='.$version;
|
||||||
//retrieves latest version of a page
|
} //wiki_make_link
|
||||||
function wiki_get_latest_page(&$entry, $pagename, $version=0) {
|
|
||||||
|
/**
|
||||||
|
* 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)."'";
|
$pagename = "'".addslashes($pagename)."'";
|
||||||
|
|
||||||
if ($version > 0 and is_int($version)) {
|
if ($version > 0 and is_int($version)) {
|
||||||
$version = "AND (version=$version)";
|
$version = "AND (version=$version)";
|
||||||
} else {
|
} else {
|
||||||
$version = '';
|
$version = '';
|
||||||
} //else
|
}
|
||||||
|
|
||||||
$select = "(pagename=$pagename) AND wiki=".$entry->id." $version ";
|
$select = "(pagename=$pagename) AND wiki=".$entry->id." $version ";
|
||||||
$sort = 'version DESC';
|
$sort = 'version DESC';
|
||||||
|
@ -64,24 +91,31 @@
|
||||||
if ($result_arr = get_records_select('wiki_pages', $select, $sort, '*', 0, 1)) {
|
if ($result_arr = get_records_select('wiki_pages', $select, $sort, '*', 0, 1)) {
|
||||||
foreach ($result_arr as $obj) {
|
foreach ($result_arr as $obj) {
|
||||||
$result_obj = $obj;
|
$result_obj = $obj;
|
||||||
} //foreach
|
}
|
||||||
} //if
|
}
|
||||||
|
|
||||||
if (isset($result_obj)) {
|
if (isset($result_obj)) {
|
||||||
$result_obj->meta = @unserialize($result_obj->meta);
|
$result_obj->meta = @unserialize($result_obj->meta);
|
||||||
return $result_obj;
|
return $result_obj;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
} //else
|
}
|
||||||
} //wiki_get_latest_page
|
} //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);
|
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) {
|
* fetches all the latest versions of all the pages
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function wiki_get_latest_pages(&$entry) {
|
||||||
//== (My)SQL for this
|
//== (My)SQL for this
|
||||||
/* select * from wiki_pages
|
/* select * from wiki_pages
|
||||||
inner join
|
inner join
|
||||||
|
@ -93,66 +127,120 @@
|
||||||
$pages = array();
|
$pages = array();
|
||||||
|
|
||||||
//http://moodle.org/bugs/bug.php?op=show&bugid=5877&pos=0
|
//http://moodle.org/bugs/bug.php?op=show&bugid=5877&pos=0
|
||||||
//if ($ids = get_records('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) {
|
if ($ids = get_records('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) {
|
||||||
if ($rs = get_recordset('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) {
|
if ($pagesets = get_records('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) {
|
||||||
$ids = $rs->GetRows();
|
foreach ($pagesets as $aPageset) {
|
||||||
//--
|
$pages[] = wiki_get_latest_page($entry, $aPageset->id);
|
||||||
foreach ($ids as $id) {
|
}
|
||||||
$pages[] = wiki_get_latest_page($entry, $id[0]);
|
|
||||||
} //foreach
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
} //else
|
}
|
||||||
|
}
|
||||||
return $pages;
|
return $pages;
|
||||||
} //wiki_get_latest_pages
|
} //wiki_get_latest_pages
|
||||||
|
|
||||||
function wiki_iterator() {
|
/**
|
||||||
return get_all_instances_in_courses("wiki", get_courses());
|
* part of search engine API
|
||||||
} //wiki_iterator
|
*
|
||||||
|
*/
|
||||||
|
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();
|
$documents = array();
|
||||||
|
|
||||||
$entries = wiki_get_entries($wiki);
|
$entries = wiki_get_entries($wiki);
|
||||||
foreach($entries as $entry) {
|
foreach($entries as $entry) {
|
||||||
|
$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
|
//all pages
|
||||||
//$pages = wiki_get_pages($entry);
|
//$pages = wiki_get_pages($entry);
|
||||||
|
|
||||||
//latest pages
|
//latest pages
|
||||||
$pages = wiki_get_latest_pages($entry);
|
$pages = wiki_get_latest_pages($entry);
|
||||||
|
|
||||||
if (is_array($pages)) {
|
if (is_array($pages)) {
|
||||||
foreach($pages as $page) {
|
foreach($pages as $page) {
|
||||||
if (strlen($page->content) > 0) {
|
if (strlen($page->content) > 0) {
|
||||||
$documents[] = new WikiSearchDocument($page, $entry->wikiid, $entry->course, $entry->groupid);
|
$documents[] = new WikiSearchDocument(get_object_vars($page), $entry->wikiid, $entry->course, $entry->groupid, $page->userid, $context->id);
|
||||||
} //if
|
}
|
||||||
} //foreach
|
}
|
||||||
} //if
|
}
|
||||||
} //foreach
|
}
|
||||||
|
|
||||||
return $documents;
|
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) {
|
* returns a single wiki search document based on a wiki_entry id
|
||||||
$pages = get_recordset('wiki_pages', 'id', $id);
|
* @param id the id of the wiki
|
||||||
$page = $pages->fields;
|
* @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']);
|
//returns the var names needed to build a sql query for addition/deletions
|
||||||
} //wiki_single_document
|
function wiki_db_names() {
|
||||||
|
|
||||||
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() {
|
|
||||||
//[primary id], [table name], [time created field name], [time modified field name]
|
//[primary id], [table name], [time created field name], [time modified field name]
|
||||||
return array('id', 'wiki_pages', 'created', 'lastmodified');
|
return array(array('id', 'wiki_pages', 'created', 'lastmodified', 'standard'));
|
||||||
} //wiki_db_names
|
} //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
|
||||||
?>
|
?>
|
|
@ -1,8 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
/* Entry page for /search
|
/*
|
||||||
* Redirects to query.php, because that is the most likely place a
|
* Entry page for /search
|
||||||
* user intended to go to when typing moodle.site/search
|
* Redirects to query.php, because that is the most likely place a
|
||||||
* */
|
* user intended to go to when typing moodle.site/search
|
||||||
|
**/
|
||||||
|
|
||||||
header("Location: query.php");
|
header("Location: query.php");
|
||||||
?>
|
?>
|
|
@ -1,106 +1,132 @@
|
||||||
<?php
|
<?php
|
||||||
/* The indexer logic -
|
/**
|
||||||
* Look through each installed module's search document class file (/search/documents)
|
* Global Search Engine for Moodle
|
||||||
* for necessary search functions, and if they're present add the content to the index.
|
* Michael Champanis (mchampan) [cynnical@gmail.com]
|
||||||
* Repeat this for blocks.
|
* review 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||||
*
|
* 2007/08/02
|
||||||
* Because the iterator/retrieval functions are now stored in /search/documents/mod_document.php,
|
*
|
||||||
* /mod/mod/lib.php doesn't have to be modified - and thus the search module becomes quite
|
* The indexer logic -
|
||||||
* self-sufficient. URL's are now stored in the index, stopping us from needing to require
|
*
|
||||||
* the class files to generate a results page.
|
* Look through each installed module's or block's search document class file (/search/documents)
|
||||||
*
|
* for necessary search functions, and if they're present add the content to the index.
|
||||||
* Along with the index data, each document's summary gets stored in the database
|
* Repeat this for blocks.
|
||||||
* and synchronised to the index (flat file) via the primary key ('id') which is mapped
|
*
|
||||||
* to the 'db_id' field in the index
|
* Because the iterator/retrieval functions are now stored in /search/documents/<mod>_document.php,
|
||||||
* */
|
* /mod/mod/lib.php doesn't have to be modified - and thus the search module becomes quite
|
||||||
|
* self-sufficient. URL's are now stored in the index, stopping us from needing to require
|
||||||
|
* the class files to generate a results page.
|
||||||
|
*
|
||||||
|
* Along with the index data, each document's summary gets stored in the database
|
||||||
|
* and synchronised to the index (flat file) via the primary key ('id') which is mapped
|
||||||
|
* to the 'dbid' field in the index
|
||||||
|
* */
|
||||||
|
|
||||||
//this'll take some time, set up the environment
|
//this'll take some time, set up the environment
|
||||||
@set_time_limit(0);
|
@set_time_limit(0);
|
||||||
@ob_implicit_flush(true);
|
@ob_implicit_flush(true);
|
||||||
@ob_end_flush();
|
@ob_end_flush();
|
||||||
|
|
||||||
require_once('../config.php');
|
require_once('../config.php');
|
||||||
require_once("$CFG->dirroot/search/lib.php");
|
require_once("$CFG->dirroot/search/lib.php");
|
||||||
|
|
||||||
//only administrators can index the moodle installation, because access to all pages is required
|
//only administrators can index the moodle installation, because access to all pages is required
|
||||||
require_login();
|
require_login();
|
||||||
|
|
||||||
if (empty($CFG->enableglobalsearch)) {
|
if (empty($CFG->enableglobalsearch)) {
|
||||||
error('Global searching is not enabled.');
|
error(get_string('globalsearchdisabled', 'search'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isadmin()) {
|
if (!isadmin()) {
|
||||||
error("You need to be an admin user to use this page.", "$CFG->wwwroot/login/index.php");
|
error(get_string('beadmin', 'search'), "$CFG->wwwroot/login/index.php");
|
||||||
} //if
|
} //if
|
||||||
|
|
||||||
//confirmation flag to prevent accidental reindexing (indexersplash.php is the correct entry point)
|
//confirmation flag to prevent accidental reindexing (indexersplash.php is the correct entry point)
|
||||||
$sure = strtolower(optional_param('areyousure', '', PARAM_ALPHA));
|
$sure = strtolower(optional_param('areyousure', '', PARAM_ALPHA));
|
||||||
|
|
||||||
if ($sure != 'yes') {
|
if ($sure != 'yes') {
|
||||||
mtrace("<pre>Sorry, you need to confirm indexing via <a href='indexersplash.php'>indexersplash.php</a>"
|
mtrace("<pre>Sorry, you need to confirm indexing via <a href='indexersplash.php'>indexersplash.php</a>"
|
||||||
.". (<a href='index.php'>Back to query page</a>).</pre>");
|
.". (<a href='index.php'>Back to query page</a>).</pre>");
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
} //if
|
} //if
|
||||||
|
|
||||||
//check for php5 (lib.php)
|
//check for php5 (lib.php)
|
||||||
if (!search_check_php5()) {
|
if (!search_check_php5()) {
|
||||||
$phpversion = phpversion();
|
$phpversion = phpversion();
|
||||||
mtrace("Sorry, global search requires PHP 5.0.0 or later (currently using version $phpversion)");
|
mtrace("Sorry, global search requires PHP 5.0.0 or later (currently using version $phpversion)");
|
||||||
exit(0);
|
exit(0);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//php5 found, continue including php5-only files
|
//php5 found, continue including php5-only files
|
||||||
//require_once("$CFG->dirroot/search/Zend/Search/Lucene.php");
|
//require_once("$CFG->dirroot/search/Zend/Search/Lucene.php");
|
||||||
require_once("$CFG->dirroot/search/indexlib.php");
|
require_once("$CFG->dirroot/search/indexlib.php");
|
||||||
|
|
||||||
mtrace('<pre>Server Time: '.date('r',time())."\n");
|
mtrace('<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8" /></head><body>');
|
||||||
|
mtrace('<pre>Server Time: '.date('r',time())."\n");
|
||||||
|
|
||||||
if ($CFG->search_indexer_busy == '1') {
|
if ($CFG->search_indexer_busy == '1') {
|
||||||
//means indexing was not finished previously
|
//means indexing was not finished previously
|
||||||
mtrace("Warning: Indexing was not successfully completed last time, restarting.\n");
|
mtrace("Warning: Indexing was not successfully completed last time, restarting.\n");
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//turn on busy flag
|
//turn on busy flag
|
||||||
set_config('search_indexer_busy', '1');
|
set_config('search_indexer_busy', '1');
|
||||||
|
|
||||||
//paths
|
//paths
|
||||||
$index_path = SEARCH_INDEX_PATH;
|
$index_path = SEARCH_INDEX_PATH;
|
||||||
$index_db_file = "$CFG->dirroot/search/db/$CFG->dbtype.sql";
|
$index_db_file = "{$CFG->dirroot}/search/db/$CFG->dbtype.sql";
|
||||||
$dbcontrol = new IndexDBControl();
|
$dbcontrol = new IndexDBControl();
|
||||||
|
|
||||||
//setup directory in data root
|
//setup directory in data root
|
||||||
if (!file_exists($index_path)) {
|
if (!file_exists($index_path)) {
|
||||||
mtrace("Data directory ($index_path) does not exist, attempting to create.");
|
mtrace("Data directory ($index_path) does not exist, attempting to create.");
|
||||||
if (!mkdir($index_path)) {
|
if (!mkdir($index_path)) {
|
||||||
search_pexit("Error creating data directory at: $index_path. Please correct.");
|
search_pexit("Error creating data directory at: $index_path. Please correct.");
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
mtrace("Directory successfully created.");
|
mtrace("Directory successfully created.");
|
||||||
} //else
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
mtrace("Using $index_path as data directory.");
|
mtrace("Using $index_path as data directory.");
|
||||||
} //else
|
}
|
||||||
|
|
||||||
$index = new Zend_Search_Lucene($index_path, true);
|
$index = new Zend_Search_Lucene($index_path, true);
|
||||||
|
|
||||||
if (!$dbcontrol->checkDB()) {
|
if (!$dbcontrol->checkDB()) {
|
||||||
search_pexit("Database error. Please check settings/files.");
|
search_pexit("Database error. Please check settings/files.");
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//begin timer
|
//begin timer
|
||||||
search_stopwatch();
|
search_stopwatch();
|
||||||
mtrace("Starting activity modules\n");
|
mtrace("Starting activity modules\n");
|
||||||
|
|
||||||
//the presence of the required search functions -
|
//the presence of the required search functions -
|
||||||
// * mod_iterator
|
// * mod_iterator
|
||||||
// * mod_get_content_for_index
|
// * mod_get_content_for_index
|
||||||
//are the sole basis for including a module in the index at the moment.
|
//are the sole basis for including a module in the index at the moment.
|
||||||
|
$searchables = array();
|
||||||
|
|
||||||
if ($mods = get_records_select('modules' /*'index this module?' where statement*/)) {
|
// collects modules
|
||||||
//add virtual modules onto the back of the array
|
if ($mods = get_records('modules', '', '', '', 'id,name')) {
|
||||||
$mods = array_merge($mods, search_get_additional_modules());
|
$searchables = array_merge($searchables, $mods);
|
||||||
|
}
|
||||||
|
mtrace(count($searchables).' modules found.');
|
||||||
|
|
||||||
foreach ($mods as $mod) {
|
// collects blocks as indexable information may be found in blocks either
|
||||||
|
if ($blocks = get_records('block', '', '', '', 'id,name')) {
|
||||||
|
// prepend the "block_" prefix to discriminate document type plugins
|
||||||
|
foreach(array_keys($blocks) as $aBlockId){
|
||||||
|
$blocks[$aBlockId]->name = 'block_'.$blocks[$aBlockId]->name;
|
||||||
|
}
|
||||||
|
$searchables = array_merge($searchables, $blocks);
|
||||||
|
mtrace(count($blocks).' blocks found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
//add virtual modules onto the back of the array
|
||||||
|
$searchables = array_merge($searchables, search_get_additional_modules());
|
||||||
|
if ($searchables){
|
||||||
|
foreach ($searchables as $mod) {
|
||||||
$class_file = $CFG->dirroot.'/search/documents/'.$mod->name.'_document.php';
|
$class_file = $CFG->dirroot.'/search/documents/'.$mod->name.'_document.php';
|
||||||
|
|
||||||
if (file_exists($class_file)) {
|
if (file_exists($class_file)) {
|
||||||
|
@ -109,18 +135,16 @@
|
||||||
//build function names
|
//build function names
|
||||||
$iter_function = $mod->name.'_iterator';
|
$iter_function = $mod->name.'_iterator';
|
||||||
$index_function = $mod->name.'_get_content_for_index';
|
$index_function = $mod->name.'_get_content_for_index';
|
||||||
|
|
||||||
$counter = 0;
|
$counter = 0;
|
||||||
$doc = new stdClass;
|
|
||||||
|
|
||||||
if (function_exists($index_function) && function_exists($iter_function)) {
|
if (function_exists($index_function) && function_exists($iter_function)) {
|
||||||
mtrace("Processing module function $index_function ...");
|
mtrace("Processing module function $index_function ...");
|
||||||
|
$sources = $iter_function();
|
||||||
foreach ($iter_function() as $i) {
|
if ($sources){
|
||||||
|
foreach ($sources as $i) {
|
||||||
$documents = $index_function($i);
|
$documents = $index_function($i);
|
||||||
|
|
||||||
//begin transaction
|
//begin transaction
|
||||||
|
if ($documents){
|
||||||
foreach($documents as $document) {
|
foreach($documents as $document) {
|
||||||
$counter++;
|
$counter++;
|
||||||
|
|
||||||
|
@ -134,43 +158,40 @@
|
||||||
$index->addDocument($document);
|
$index->addDocument($document);
|
||||||
|
|
||||||
//commit every x new documents, and print a status message
|
//commit every x new documents, and print a status message
|
||||||
if (($counter%2000) == 0) {
|
if (($counter % 2000) == 0) {
|
||||||
$index->commit();
|
$index->commit();
|
||||||
mtrace(".. $counter");
|
mtrace(".. $counter");
|
||||||
} //if
|
}
|
||||||
} //foreach
|
}
|
||||||
|
}
|
||||||
//end transaction
|
//end transaction
|
||||||
|
}
|
||||||
} //foreach
|
}
|
||||||
|
|
||||||
//commit left over documents, and finish up
|
//commit left over documents, and finish up
|
||||||
$index->commit();
|
$index->commit();
|
||||||
|
|
||||||
mtrace("-- $counter documents indexed");
|
mtrace("-- $counter documents indexed");
|
||||||
mtrace("done.\n");
|
mtrace("done.\n");
|
||||||
} //if
|
}
|
||||||
} //if
|
}
|
||||||
} //foreach
|
}
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//finished modules
|
//finished modules
|
||||||
mtrace('Finished activity modules');
|
mtrace('Finished activity modules');
|
||||||
search_stopwatch();
|
search_stopwatch();
|
||||||
|
|
||||||
//now blocks...
|
mtrace(".<br/><a href='index.php'>Back to query page</a>.");
|
||||||
//
|
mtrace('</pre>');
|
||||||
|
|
||||||
mtrace(".<br/><a href='index.php'>Back to query page</a>.");
|
//finished, turn busy flag off
|
||||||
mtrace('</pre>');
|
set_config("search_indexer_busy", "0");
|
||||||
|
|
||||||
//finished, turn busy flag off
|
//mark the time we last updated
|
||||||
set_config("search_indexer_busy", "0");
|
set_config("search_indexer_run_date", time());
|
||||||
|
|
||||||
//mark the time we last updated
|
//and the index size
|
||||||
set_config("search_indexer_run_date", time());
|
set_config("search_index_size", (int)$index->count());
|
||||||
|
|
||||||
//and the index size
|
|
||||||
set_config("search_index_size", (int)$index->count());
|
|
||||||
|
|
||||||
?>
|
?>
|
|
@ -1,33 +1,39 @@
|
||||||
<?php
|
<?php
|
||||||
/* This file serves as a splash-screen (entry page) to the indexer script -
|
/**
|
||||||
* it is in place to prevent accidental reindexing which can lead to a loss
|
* Global Search Engine for Moodle
|
||||||
* of time, amongst other things.
|
* Michael Champanis (mchampan) [cynnical@gmail.com]
|
||||||
* */
|
* review 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||||
|
* 2007/08/02
|
||||||
|
*
|
||||||
|
* This file serves as a splash-screen (entry page) to the indexer script -
|
||||||
|
* it is in place to prevent accidental reindexing which can lead to a loss
|
||||||
|
* of time, amongst other things.
|
||||||
|
**/
|
||||||
|
|
||||||
require_once('../config.php');
|
require_once('../config.php');
|
||||||
require_once("$CFG->dirroot/search/lib.php");
|
require_once("$CFG->dirroot/search/lib.php");
|
||||||
|
|
||||||
require_login();
|
require_login();
|
||||||
|
|
||||||
if (empty($CFG->enableglobalsearch)) {
|
if (empty($CFG->enableglobalsearch)) {
|
||||||
error('Global searching is not enabled.');
|
error(get_string('globalsearchdisabled', 'search'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isadmin()) {
|
if (!isadmin()) {
|
||||||
error("You need to be an admin user to use this page.", "$CFG->wwwroot/login/index.php");
|
error(get_string('beadmin', 'search'), "$CFG->wwwroot/login/index.php");
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//check for php5 (lib.php)
|
//check for php5 (lib.php)
|
||||||
if (!search_check_php5()) {
|
if (!search_check_php5()) {
|
||||||
$phpversion = phpversion();
|
$phpversion = phpversion();
|
||||||
mtrace("Sorry, global search requires PHP 5.0.0 or later (currently using version $phpversion)");
|
mtrace("Sorry, global search requires PHP 5.0.0 or later (currently using version $phpversion)");
|
||||||
exit(0);
|
exit(0);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
require_once("$CFG->dirroot/search/indexlib.php");
|
require_once("$CFG->dirroot/search/indexlib.php");
|
||||||
$indexinfo = new IndexInfo();
|
$indexinfo = new IndexInfo();
|
||||||
|
|
||||||
if ($indexinfo->valid()) {
|
if ($indexinfo->valid()) {
|
||||||
mtrace("<pre>The data directory ($indexinfo->path) contains $indexinfo->filecount files, and\n"
|
mtrace("<pre>The data directory ($indexinfo->path) contains $indexinfo->filecount files, and\n"
|
||||||
."there are ".$indexinfo->dbcount." records in the <em>search_documents</em> table.\n"
|
."there are ".$indexinfo->dbcount." records in the <em>search_documents</em> table.\n"
|
||||||
."\n"
|
."\n"
|
||||||
|
@ -42,7 +48,8 @@
|
||||||
."<a href='tests/index.php'>Test indexing</a> or "
|
."<a href='tests/index.php'>Test indexing</a> or "
|
||||||
."<a href='indexer.php?areyousure=yes'>Continue indexing</a> or <a href='index.php'>Back to query page</a>."
|
."<a href='indexer.php?areyousure=yes'>Continue indexing</a> or <a href='index.php'>Back to query page</a>."
|
||||||
."</pre>");
|
."</pre>");
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
header('Location: indexer.php?areyousure=yes');
|
header('Location: indexer.php?areyousure=yes');
|
||||||
} //else
|
}
|
||||||
?>
|
?>
|
|
@ -1,15 +1,20 @@
|
||||||
<?php
|
<?php
|
||||||
/* Index info class
|
/*
|
||||||
*
|
* Author: Michael Champanis
|
||||||
* Used to retrieve information about an index.
|
*
|
||||||
* Has methods to check for valid database and data directory,
|
* Reviewed by: Valery Fremaux (2007)
|
||||||
* and the index itself.
|
*
|
||||||
* */
|
* Index info class
|
||||||
|
*
|
||||||
|
* Used to retrieve information about an index.
|
||||||
|
* Has methods to check for valid database and data directory,
|
||||||
|
* and the index itself.
|
||||||
|
**/
|
||||||
|
|
||||||
require_once("$CFG->dirroot/search/lib.php");
|
require_once("$CFG->dirroot/search/lib.php");
|
||||||
require_once("$CFG->dirroot/search/Zend/Search/Lucene.php");
|
require_once("$CFG->dirroot/search/Zend/Search/Lucene.php");
|
||||||
|
|
||||||
class IndexInfo {
|
class IndexInfo {
|
||||||
private $path, //index data directory
|
private $path, //index data directory
|
||||||
$size, //size of directory (i.e. the whole index)
|
$size, //size of directory (i.e. the whole index)
|
||||||
$filecount, //number of files
|
$filecount, //number of files
|
||||||
|
@ -19,7 +24,7 @@
|
||||||
$complete, //is index completely formed?
|
$complete, //is index completely formed?
|
||||||
$time; //date index was generated
|
$time; //date index was generated
|
||||||
|
|
||||||
public function __construct($path=SEARCH_INDEX_PATH) {
|
public function __construct($path = SEARCH_INDEX_PATH) {
|
||||||
global $CFG, $db;
|
global $CFG, $db;
|
||||||
|
|
||||||
$this->path = $path;
|
$this->path = $path;
|
||||||
|
@ -38,11 +43,12 @@
|
||||||
$index_dir = get_directory_list($this->path, '', false, false);
|
$index_dir = get_directory_list($this->path, '', false, false);
|
||||||
$this->filecount = count($index_dir);
|
$this->filecount = count($index_dir);
|
||||||
$this->indexcount = $test_index->count();
|
$this->indexcount = $test_index->count();
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$this->size = 0;
|
$this->size = 0;
|
||||||
$this->filecount = 0;
|
$this->filecount = 0;
|
||||||
$this->indexcount = 0;
|
$this->indexcount = 0;
|
||||||
} //else
|
}
|
||||||
|
|
||||||
$db_exists = false; //for now
|
$db_exists = false; //for now
|
||||||
|
|
||||||
|
@ -66,126 +72,155 @@
|
||||||
foreach($types as $type) {
|
foreach($types as $type) {
|
||||||
$c = count_records(SEARCH_DATABASE_TABLE, 'doctype', $type);
|
$c = count_records(SEARCH_DATABASE_TABLE, 'doctype', $type);
|
||||||
$this->types[$type] = (int)$c;
|
$this->types[$type] = (int)$c;
|
||||||
} //foreach
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$this->dbcount = 0;
|
$this->dbcount = 0;
|
||||||
$this->types = array();
|
$this->types = array();
|
||||||
} //else
|
}
|
||||||
|
|
||||||
//check if the busy flag is set
|
//check if the busy flag is set
|
||||||
if ($CFG->search_indexer_busy == '1') {
|
if ($CFG->search_indexer_busy == '1') {
|
||||||
$this->complete = false;
|
$this->complete = false;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$this->complete = true;
|
$this->complete = true;
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//get the last run date for the indexer
|
//get the last run date for the indexer
|
||||||
if ($this->valid() && $CFG->search_indexer_run_date) {
|
if ($this->valid() && $CFG->search_indexer_run_date) {
|
||||||
$this->time = $CFG->search_indexer_run_date;
|
$this->time = $CFG->search_indexer_run_date;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$this->time = 0;
|
$this->time = 0;
|
||||||
} //else
|
}
|
||||||
} //__construct
|
} //__construct
|
||||||
|
|
||||||
//returns false on error, and the error message via referenced variable $err
|
/**
|
||||||
public function valid(&$err=null) {
|
* returns false on error, and the error message via referenced variable $err
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function valid(&$err = null) {
|
||||||
$err = array();
|
$err = array();
|
||||||
$ret = true;
|
$ret = true;
|
||||||
|
|
||||||
if (!$this->is_valid_dir()) {
|
if (!$this->is_valid_dir()) {
|
||||||
$err['dir'] = 'Index directory either contains an invalid index, or nothing at all.';
|
$err['dir'] = get_string('invalidindexerror', 'search');
|
||||||
$ret = false;
|
$ret = false;
|
||||||
} //if
|
}
|
||||||
|
|
||||||
if (!$this->is_valid_db()) {
|
if (!$this->is_valid_db()) {
|
||||||
$err['db'] = 'Database table is not present, or contains no index records.';
|
$err['db'] = get_string('emptydatabaseerror', 'search');
|
||||||
$ret = false;
|
$ret = false;
|
||||||
} //if
|
}
|
||||||
|
|
||||||
if (!$this->complete) {
|
if (!$this->complete) {
|
||||||
$err['index'] = 'Indexing was not successfully completed, please restart it.';
|
$err['index'] = get_string('uncompleteindexingerror','search');
|
||||||
$ret = false;
|
$ret = false;
|
||||||
} //if
|
}
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
} //valid
|
} //valid
|
||||||
|
|
||||||
//is the index dir valid
|
/**
|
||||||
|
* is the index dir valid
|
||||||
|
*
|
||||||
|
*/
|
||||||
public function is_valid_dir() {
|
public function is_valid_dir() {
|
||||||
if ($this->filecount > 0) {
|
if ($this->filecount > 0) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return false;
|
return false;
|
||||||
} //else
|
}
|
||||||
} //is_valid_dir
|
} //is_valid_dir
|
||||||
|
|
||||||
//is the db table valid
|
/**
|
||||||
|
* is the db table valid
|
||||||
|
*
|
||||||
|
*/
|
||||||
public function is_valid_db() {
|
public function is_valid_db() {
|
||||||
if ($this->dbcount > 0) {
|
if ($this->dbcount > 0) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return false;
|
return false;
|
||||||
} //else
|
}
|
||||||
} //is_valid_db
|
} //is_valid_db
|
||||||
|
|
||||||
//shorthand get method for the class variables
|
/**
|
||||||
|
* shorthand get method for the class variables
|
||||||
|
*
|
||||||
|
*/
|
||||||
public function __get($var) {
|
public function __get($var) {
|
||||||
if (in_array($var, array_keys(get_class_vars(get_class($this))))) {
|
if (in_array($var, array_keys(get_class_vars(get_class($this))))) {
|
||||||
return $this->$var;
|
return $this->$var;
|
||||||
} //if
|
}
|
||||||
} //__get
|
} //__get
|
||||||
} //IndexInfo
|
} //IndexInfo
|
||||||
|
|
||||||
|
|
||||||
/* DB Index control class
|
/*
|
||||||
|
* DB Index control class
|
||||||
|
*
|
||||||
|
* Used to control the search index database table
|
||||||
|
**/
|
||||||
|
class IndexDBControl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* does the table exist?
|
||||||
*
|
*
|
||||||
* Used to control the search index database table
|
*/
|
||||||
* */
|
|
||||||
|
|
||||||
class IndexDBControl {
|
|
||||||
//does the table exist?
|
|
||||||
public function checkTableExists() {
|
public function checkTableExists() {
|
||||||
global $CFG, $db;
|
global $CFG, $db;
|
||||||
|
|
||||||
$table = SEARCH_DATABASE_TABLE;
|
$table = SEARCH_DATABASE_TABLE;
|
||||||
$tables = $db->MetaTables();
|
$tables = $db->MetaTables();
|
||||||
|
|
||||||
if (in_array($CFG->prefix.$table, $tables)) {
|
if (in_array($CFG->prefix.$table, $tables)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return false;
|
return false;
|
||||||
} //else
|
}
|
||||||
} //checkTableExists
|
} //checkTableExists
|
||||||
|
|
||||||
//is our database setup valid?
|
/**
|
||||||
|
* is our database setup valid?
|
||||||
|
*
|
||||||
|
*/
|
||||||
public function checkDB() {
|
public function checkDB() {
|
||||||
global $CFG, $db;
|
global $CFG, $db;
|
||||||
|
|
||||||
$sqlfile = "$CFG->dirroot/search/db/$CFG->dbtype.sql";
|
$sqlfile = "$CFG->dirroot/blocks/search/db/$CFG->dbtype.sql";
|
||||||
$ret = false;
|
$ret = false;
|
||||||
|
|
||||||
if ($this->checkTableExists()) {
|
if ($this->checkTableExists()) {
|
||||||
execute_sql('drop table '.$CFG->prefix.SEARCH_DATABASE_TABLE, false);
|
execute_sql('drop table '.$CFG->prefix.SEARCH_DATABASE_TABLE, false);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
ob_start(); //turn output buffering on - to hide modify_database() output
|
//turn output buffering on - to hide modify_database() output
|
||||||
|
ob_start();
|
||||||
$ret = modify_database($sqlfile, '', false);
|
$ret = modify_database($sqlfile, '', false);
|
||||||
ob_end_clean(); //chuck the buffer and resume normal operation
|
|
||||||
|
|
||||||
|
//chuck the buffer and resume normal operation
|
||||||
|
ob_end_clean();
|
||||||
return $ret;
|
return $ret;
|
||||||
} //checkDB
|
} //checkDB
|
||||||
|
|
||||||
//add a document record to the table
|
/**
|
||||||
|
* add a document record to the table
|
||||||
|
* @param document must be a Lucene SearchDocument instance
|
||||||
|
*/
|
||||||
public function addDocument($document=null) {
|
public function addDocument($document=null) {
|
||||||
global $db;
|
global $db, $CFG;
|
||||||
|
|
||||||
if ($document == null) {
|
if ($document == null) {
|
||||||
return false;
|
return false;
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//object to insert into db
|
// object to insert into db
|
||||||
$doc->doctype = $document->doctype;
|
$doc->doctype = $document->doctype;
|
||||||
$doc->docid = $document->docid;
|
$doc->docid = $document->docid;
|
||||||
|
$doc->itemtype = $document->itemtype;
|
||||||
$doc->title = search_escape_string($document->title);
|
$doc->title = search_escape_string($document->title);
|
||||||
$doc->url = search_escape_string($document->url);
|
$doc->url = search_escape_string($document->url);
|
||||||
$doc->update = time();
|
$doc->update = time();
|
||||||
|
@ -199,12 +234,15 @@
|
||||||
return $id;
|
return $id;
|
||||||
} //addDocument
|
} //addDocument
|
||||||
|
|
||||||
//remove a document record from the index
|
/**
|
||||||
|
* remove a document record from the index
|
||||||
|
* @param document must be a Lucene document instance, or at least a dbid enveloppe
|
||||||
|
*/
|
||||||
public function delDocument($document) {
|
public function delDocument($document) {
|
||||||
global $db;
|
global $db;
|
||||||
|
|
||||||
delete_records(SEARCH_DATABASE_TABLE, 'id', $document->dbid);
|
delete_records(SEARCH_DATABASE_TABLE, 'id', $document->dbid);
|
||||||
} //delDocument
|
} //delDocument
|
||||||
} //IndexControl
|
} //IndexControl
|
||||||
|
|
||||||
?>
|
?>
|
169
search/lib.php
169
search/lib.php
|
@ -1,61 +1,95 @@
|
||||||
<?php
|
<?php
|
||||||
|
/*
|
||||||
|
* Author: Michael Champanis
|
||||||
|
*
|
||||||
|
* This file must not contain any PHP 5, because it is used to test for PHP 5
|
||||||
|
* itself, and needs to be able to be executed on PHP 4 installations.
|
||||||
|
*
|
||||||
|
* Reviewed by: Valery Fremaux (2007)
|
||||||
|
* - adding techproject search capabilities
|
||||||
|
* - adding full internationalization
|
||||||
|
**/
|
||||||
|
|
||||||
/* Move this stuff to lib/searchlib.php?
|
/*
|
||||||
* Author: Michael Champanis
|
// function reference
|
||||||
*
|
function search_get_document_types($prefix = 'SEARCH_TYPE_') {
|
||||||
* This file must not contain any PHP 5, because it is used to test for PHP 5
|
function search_get_additional_modules() {
|
||||||
* itself, and needs to be able to be executed on PHP 4 installations.
|
function search_shorten_url($url, $length=30) {
|
||||||
* */
|
function search_escape_string($str) {
|
||||||
|
function search_check_php5($feedback = false) {
|
||||||
|
function search_stopwatch($cli = false) {
|
||||||
|
function search_pexit($str = "") {
|
||||||
|
*/
|
||||||
|
|
||||||
define('SEARCH_INDEX_PATH', "$CFG->dataroot/search");
|
define('SEARCH_INDEX_PATH', "$CFG->dataroot/search");
|
||||||
define('SEARCH_DATABASE_TABLE', 'search_documents');
|
define('SEARCH_DATABASE_TABLE', 'search_documents');
|
||||||
|
|
||||||
//document types that can be searched
|
//document types that can be searched
|
||||||
//define('SEARCH_TYPE_NONE', 'none');
|
//define('SEARCH_TYPE_NONE', 'none');
|
||||||
define('SEARCH_TYPE_WIKI', 'wiki');
|
define('SEARCH_TYPE_WIKI', 'wiki');
|
||||||
define('SEARCH_TYPE_FORUM', 'forum');
|
define('PATH_FOR_SEARCH_TYPE_WIKI', 'mod/wiki');
|
||||||
define('SEARCH_TYPE_GLOSSARY', 'glossary');
|
define('SEARCH_TYPE_FORUM', 'forum');
|
||||||
define('SEARCH_TYPE_RESOURCE', 'resource');
|
define('PATH_FOR_SEARCH_TYPE_FORUM', 'mod/forum');
|
||||||
|
define('SEARCH_TYPE_GLOSSARY', 'glossary');
|
||||||
|
define('PATH_FOR_SEARCH_TYPE_GLOSSARY', 'mod/glossary');
|
||||||
|
define('SEARCH_TYPE_RESOURCE', 'resource');
|
||||||
|
define('PATH_FOR_SEARCH_TYPE_RESOURCE', 'mod/resource');
|
||||||
|
define('SEARCH_TYPE_TECHPROJECT', 'techproject');
|
||||||
|
define('PATH_FOR_SEARCH_TYPE_TECHPROJECT', 'mod/techproject');
|
||||||
|
define('SEARCH_TYPE_DATA', 'data');
|
||||||
|
define('PATH_FOR_SEARCH_TYPE_DATA', 'mod/data');
|
||||||
|
define('SEARCH_TYPE_CHAT', 'chat');
|
||||||
|
define('PATH_FOR_SEARCH_TYPE_CHAT', 'mod/chat');
|
||||||
|
|
||||||
//returns all the document type constants
|
/**
|
||||||
function search_get_document_types($prefix='SEARCH_TYPE') {
|
* returns all the document type constants
|
||||||
|
* @param prefix a pattern for recognizing constants
|
||||||
|
* @return an array of type labels
|
||||||
|
*/
|
||||||
|
function search_get_document_types($prefix = 'SEARCH_TYPE_') {
|
||||||
$ret = array();
|
$ret = array();
|
||||||
|
foreach (get_defined_constants() as $key => $value) {
|
||||||
foreach (get_defined_constants() as $key=>$value) {
|
if (preg_match("/^{$prefix}/", $key)){
|
||||||
if (substr($key, 0, strlen($prefix)) == $prefix) {
|
|
||||||
$ret[$key] = $value;
|
$ret[$key] = $value;
|
||||||
} //if
|
}
|
||||||
} //foreach
|
}
|
||||||
|
|
||||||
sort($ret);
|
sort($ret);
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
} //search_get_document_types
|
} //search_get_document_types
|
||||||
|
|
||||||
// additional virtual modules to index
|
/**
|
||||||
//
|
* additional virtual modules to index
|
||||||
// By adding 'moo' to the extras array, an additional document type
|
*
|
||||||
// documents/moo_document.php will be indexed - this allows for
|
* By adding 'moo' to the extras array, an additional document type
|
||||||
// virtual modules to be added to the index, i.e. non-module specific
|
* documents/moo_document.php will be indexed - this allows for
|
||||||
// information.
|
* virtual modules to be added to the index, i.e. non-module specific
|
||||||
function search_get_additional_modules() {
|
* information.
|
||||||
|
*/
|
||||||
|
function search_get_additional_modules() {
|
||||||
$extras = array(/* additional keywords go here */);
|
$extras = array(/* additional keywords go here */);
|
||||||
$ret = array();
|
$ret = array();
|
||||||
|
|
||||||
foreach($extras as $extra) {
|
foreach($extras as $extra) {
|
||||||
$temp->name = $extra;
|
$temp->name = $extra;
|
||||||
$ret[] = clone($temp);
|
$ret[] = clone($temp);
|
||||||
} //foreach
|
}
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
} //search_get_additional_modules
|
} //search_get_additional_modules
|
||||||
|
|
||||||
//shortens a url so it can fit on the results page
|
/**
|
||||||
function search_shorten_url($url, $length=30) {
|
* shortens a url so it can fit on the results page
|
||||||
|
* @param url the url
|
||||||
|
* @param length the size limit we want
|
||||||
|
*/
|
||||||
|
function search_shorten_url($url, $length=30) {
|
||||||
return substr($url, 0, $length)."...";
|
return substr($url, 0, $length)."...";
|
||||||
} //search_shorten_url
|
} //search_shorten_url
|
||||||
|
|
||||||
function search_escape_string($str) {
|
/**
|
||||||
|
* a local function for escaping
|
||||||
|
* @param str the string to escape
|
||||||
|
* @return the escaped string
|
||||||
|
*/
|
||||||
|
function search_escape_string($str) {
|
||||||
global $CFG;
|
global $CFG;
|
||||||
|
|
||||||
switch ($CFG->dbfamily) {
|
switch ($CFG->dbfamily) {
|
||||||
|
@ -67,47 +101,56 @@
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$s = addslashes($str);
|
$s = addslashes($str);
|
||||||
} //switch
|
}
|
||||||
|
|
||||||
return $s;
|
return $s;
|
||||||
} //search_escape_string
|
} //search_escape_string
|
||||||
|
|
||||||
//get a real php 5 version number, using 5.0.0 arbitrarily
|
/**
|
||||||
function search_check_php5($feedback=false) {
|
* get a real php 5 version number, using 5.0.0 arbitrarily
|
||||||
|
* @param feedback if true, prints a feedback message to output.
|
||||||
|
* @return true if version of PHP is high enough
|
||||||
|
*/
|
||||||
|
function search_check_php5($feedback = false) {
|
||||||
if (!check_php_version("5.0.0")) {
|
if (!check_php_version("5.0.0")) {
|
||||||
if ($feedback) {
|
if ($feedback) {
|
||||||
$phpversion = phpversion();
|
print_heading(get_string('versiontoolow', 'search'));
|
||||||
print_heading("Sorry, global search requires PHP 5.0.0 or later (currently using version $phpversion)");
|
}
|
||||||
} //if
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return true;
|
return true;
|
||||||
} //else
|
}
|
||||||
} //search_check_php5
|
} //search_check_php5
|
||||||
|
|
||||||
//simple timer function, outputs result on 2nd call
|
/**
|
||||||
function search_stopwatch($cli = false) {
|
* simple timer function, on first call, records a current microtime stamp, outputs result on 2nd call
|
||||||
|
* @param cli an output formatting switch
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function search_stopwatch($cli = false) {
|
||||||
if (!empty($GLOBALS['search_script_start_time'])) {
|
if (!empty($GLOBALS['search_script_start_time'])) {
|
||||||
if (!$cli) print '<em>';
|
if (!$cli) print '<em>';
|
||||||
print round(microtime(true) - $GLOBALS['search_script_start_time'], 6).' seconds';
|
print round(microtime(true) - $GLOBALS['search_script_start_time'], 6).' '.get_string('seconds', 'search');
|
||||||
if (!$cli) print '</em>';
|
if (!$cli) print '</em>';
|
||||||
|
|
||||||
unset($GLOBALS['search_script_start_time']);
|
unset($GLOBALS['search_script_start_time']);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$GLOBALS['search_script_start_time'] = microtime(true);
|
$GLOBALS['search_script_start_time'] = microtime(true);
|
||||||
} //else
|
}
|
||||||
} //search_stopwatch
|
} //search_stopwatch
|
||||||
|
|
||||||
//print and exit (for debugging)
|
/**
|
||||||
function search_pexit($str = "") {
|
* print and exit (for debugging)
|
||||||
|
* @param str a variable to explore
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function search_pexit($str = "") {
|
||||||
if (is_array($str) or is_object($str)) {
|
if (is_array($str) or is_object($str)) {
|
||||||
print_r($str);
|
print_r($str);
|
||||||
} else if ($str) {
|
} else if ($str) {
|
||||||
print $str."<br/>";
|
print $str."<br/>";
|
||||||
} //if
|
}
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
} //search_pexit
|
} //search_pexit
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
311
search/query.php
311
search/query.php
|
@ -1,43 +1,48 @@
|
||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* The query page - accepts a user-entered query string and returns results.
|
* Global Search Engine for Moodle
|
||||||
*
|
* Michael Champanis (mchampan) [cynnical@gmail.com]
|
||||||
* Queries are boolean-aware, e.g.:
|
* review 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||||
*
|
* 2007/08/02
|
||||||
* '+' term required
|
*
|
||||||
* '-' term must not be present
|
* The query page - accepts a user-entered query string and returns results.
|
||||||
* '' (no modifier) term's presence increases rank, but isn't required
|
*
|
||||||
* 'field:' search this field
|
* Queries are boolean-aware, e.g.:
|
||||||
*
|
*
|
||||||
* Examples:
|
* '+' term required
|
||||||
*
|
* '-' term must not be present
|
||||||
* 'earthquake +author:michael'
|
* '' (no modifier) term's presence increases rank, but isn't required
|
||||||
* Searches for documents written by 'michael' that contain 'earthquake'
|
* 'field:' search this field
|
||||||
*
|
*
|
||||||
* 'earthquake +doctype:wiki'
|
* Examples:
|
||||||
* Search all wiki pages for 'earthquake'
|
*
|
||||||
*
|
* 'earthquake +author:michael'
|
||||||
* '+author:helen +author:foster'
|
* Searches for documents written by 'michael' that contain 'earthquake'
|
||||||
* All articles written by Helen Foster
|
*
|
||||||
*
|
* 'earthquake +doctype:wiki'
|
||||||
*/
|
* Search all wiki pages for 'earthquake'
|
||||||
|
*
|
||||||
|
* '+author:helen +author:foster'
|
||||||
|
* All articles written by Helen Foster
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
require_once('../config.php');
|
require_once('../config.php');
|
||||||
require_once("$CFG->dirroot/search/lib.php");
|
require_once("$CFG->dirroot/search/lib.php");
|
||||||
|
|
||||||
if ($CFG->forcelogin) {
|
if ($CFG->forcelogin) {
|
||||||
require_login();
|
require_login();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($CFG->enableglobalsearch)) {
|
if (empty($CFG->enableglobalsearch)) {
|
||||||
error('Global searching is not enabled.');
|
error(get_string('globalsearchdisabled', 'search'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$adv = new Object();
|
$adv = new Object();
|
||||||
|
|
||||||
//check for php5, but don't die yet (see line 52)
|
// check for php5, but don't die yet (see line 52)
|
||||||
if ($check = search_check_php5()) {
|
if ($check = search_check_php5()) {
|
||||||
require_once("$CFG->dirroot/search/querylib.php");
|
require_once("{$CFG->dirroot}/search/querylib.php");
|
||||||
|
|
||||||
$page_number = optional_param('page', -1, PARAM_INT);
|
$page_number = optional_param('page', -1, PARAM_INT);
|
||||||
$pages = ($page_number == -1) ? false : true;
|
$pages = ($page_number == -1) ? false : true;
|
||||||
|
@ -45,24 +50,25 @@
|
||||||
$query_string = optional_param('query_string', '', PARAM_CLEAN);
|
$query_string = optional_param('query_string', '', PARAM_CLEAN);
|
||||||
|
|
||||||
if ($pages && isset($_SESSION['search_advanced_query'])) {
|
if ($pages && isset($_SESSION['search_advanced_query'])) {
|
||||||
//if both are set, then we are busy browsing through the result pages of an advanced query
|
// if both are set, then we are busy browsing through the result pages of an advanced query
|
||||||
$adv = unserialize($_SESSION['search_advanced_query']);
|
$adv = unserialize($_SESSION['search_advanced_query']);
|
||||||
} else if ($advanced) {
|
}
|
||||||
//otherwise we are dealing with a new advanced query
|
else if ($advanced) {
|
||||||
|
// otherwise we are dealing with a new advanced query
|
||||||
unset($_SESSION['search_advanced_query']);
|
unset($_SESSION['search_advanced_query']);
|
||||||
session_unregister('search_advanced_query');
|
session_unregister('search_advanced_query');
|
||||||
|
|
||||||
//chars to strip from strings (whitespace)
|
// chars to strip from strings (whitespace)
|
||||||
$chars = " \t\n\r\0\x0B,-+";
|
$chars = " \t\n\r\0\x0B,-+";
|
||||||
|
|
||||||
//retrieve advanced query variables
|
// retrieve advanced query variables
|
||||||
$adv->mustappear = trim(optional_param('mustappear', '', PARAM_CLEAN), $chars);
|
$adv->mustappear = trim(optional_param('mustappear', '', PARAM_CLEAN), $chars);
|
||||||
$adv->notappear = trim(optional_param('notappear', '', PARAM_CLEAN), $chars);
|
$adv->notappear = trim(optional_param('notappear', '', PARAM_CLEAN), $chars);
|
||||||
$adv->canappear = trim(optional_param('canappear', '', PARAM_CLEAN), $chars);
|
$adv->canappear = trim(optional_param('canappear', '', PARAM_CLEAN), $chars);
|
||||||
$adv->module = optional_param('module', '', PARAM_CLEAN);
|
$adv->module = optional_param('module', '', PARAM_CLEAN);
|
||||||
$adv->title = trim(optional_param('title', '', PARAM_CLEAN), $chars);
|
$adv->title = trim(optional_param('title', '', PARAM_CLEAN), $chars);
|
||||||
$adv->author = trim(optional_param('author', '', PARAM_CLEAN), $chars);
|
$adv->author = trim(optional_param('author', '', PARAM_CLEAN), $chars);
|
||||||
} //else
|
}
|
||||||
|
|
||||||
if ($advanced) {
|
if ($advanced) {
|
||||||
//parse the advanced variables into a query string
|
//parse the advanced variables into a query string
|
||||||
|
@ -70,193 +76,216 @@
|
||||||
|
|
||||||
$query_string = '';
|
$query_string = '';
|
||||||
|
|
||||||
//get all available module types
|
// get all available module types
|
||||||
$module_types = array_merge(array('All'), array_values(search_get_document_types()));
|
$module_types = array_merge(array('all'), array_values(search_get_document_types()));
|
||||||
$adv->module = in_array($adv->module, $module_types) ? $adv->module : 'All';
|
$adv->module = in_array($adv->module, $module_types) ? $adv->module : 'all';
|
||||||
|
|
||||||
//convert '1 2' into '+1 +2' for required words field
|
// convert '1 2' into '+1 +2' for required words field
|
||||||
if (strlen(trim($adv->mustappear)) > 0) {
|
if (strlen(trim($adv->mustappear)) > 0) {
|
||||||
$query_string = ' +'.implode(' +', preg_split("/[\s,;]+/", $adv->mustappear));
|
$query_string = ' +'.implode(' +', preg_split("/[\s,;]+/", $adv->mustappear));
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//convert '1 2' into '-1 -2' for not wanted words field
|
// convert '1 2' into '-1 -2' for not wanted words field
|
||||||
if (strlen(trim($adv->notappear)) > 0) {
|
if (strlen(trim($adv->notappear)) > 0) {
|
||||||
$query_string .= ' -'.implode(' -', preg_split("/[\s,;]+/", $adv->notappear));
|
$query_string .= ' -'.implode(' -', preg_split("/[\s,;]+/", $adv->notappear));
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//this field is left untouched, apart from whitespace being stripped
|
// this field is left untouched, apart from whitespace being stripped
|
||||||
if (strlen(trim($adv->canappear)) > 0) {
|
if (strlen(trim($adv->canappear)) > 0) {
|
||||||
$query_string .= ' '.implode(' ', preg_split("/[\s,;]+/", $adv->canappear));
|
$query_string .= ' '.implode(' ', preg_split("/[\s,;]+/", $adv->canappear));
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//add module restriction
|
// add module restriction
|
||||||
if ($adv->module != 'All') {
|
$doctypestr = get_string('doctype', 'search');
|
||||||
$query_string .= ' +doctype:'.$adv->module;
|
$titlestr = get_string('title', 'search');
|
||||||
} //if
|
$authorstr = get_string('author', 'search');
|
||||||
|
if ($adv->module != 'all') {
|
||||||
|
$query_string .= " +{$doctypestr}:".$adv->module;
|
||||||
|
}
|
||||||
|
|
||||||
//create title search string
|
// create title search string
|
||||||
if (strlen(trim($adv->title)) > 0) {
|
if (strlen(trim($adv->title)) > 0) {
|
||||||
$query_string .= ' +title:'.implode(' +title:', preg_split("/[\s,;]+/", $adv->title));
|
$query_string .= " +{$titlestr}:".implode(" +{$titlestr}:", preg_split("/[\s,;]+/", $adv->title));
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//create author search string
|
// create author search string
|
||||||
if (strlen(trim($adv->author)) > 0) {
|
if (strlen(trim($adv->author)) > 0) {
|
||||||
$query_string .= ' +author:'.implode(' +author:', preg_split("/[\s,;]+/", $adv->author));
|
$query_string .= " +{$authorstr}:".implode(" +{$authorstr}:", preg_split("/[\s,;]+/", $adv->author));
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//save our options if the query is valid
|
// save our options if the query is valid
|
||||||
if (!empty($query_string)) {
|
if (!empty($query_string)) {
|
||||||
$_SESSION['search_advanced_query'] = serialize($adv);
|
$_SESSION['search_advanced_query'] = serialize($adv);
|
||||||
} //if
|
}
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//normalise page number
|
// normalise page number
|
||||||
if ($page_number < 1) {
|
if ($page_number < 1) {
|
||||||
$page_number = 1;
|
$page_number = 1;
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//run the query against the index
|
//run the query against the index
|
||||||
$sq = new SearchQuery($query_string, $page_number, 10, false);
|
$sq = new SearchQuery($query_string, $page_number, 10, false);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
if (!$site = get_site()) {
|
if (!$site = get_site()) {
|
||||||
redirect("index.php");
|
redirect("index.php");
|
||||||
} //if
|
}
|
||||||
|
|
||||||
$strsearch = "Search"; //get_string();
|
$strsearch = get_string('search', 'search');
|
||||||
$strquery = "Enter your search query"; //get_string();
|
$strquery = get_string('enteryoursearchquery', 'search');
|
||||||
|
|
||||||
print_header("$site->shortname: $strsearch: $strquery", "$site->fullname",
|
print_header("$site->shortname: $strsearch: $strquery", "$site->fullname",
|
||||||
"<a href=\"index.php\">$strsearch</a> -> $strquery");
|
"<a href=\"index.php\">$strsearch</a> -> $strquery");
|
||||||
|
|
||||||
//keep things pretty, even if php5 isn't available
|
//keep things pretty, even if php5 isn't available
|
||||||
if (!$check) {
|
if (!$check) {
|
||||||
print_heading(search_check_php5(true));
|
print_heading(search_check_php5(true));
|
||||||
print_footer();
|
print_footer();
|
||||||
exit(0);
|
exit(0);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
print_simple_box_start('center', '100%', '', 20);
|
print_box_start();
|
||||||
print_heading($strquery);
|
print_heading($strquery);
|
||||||
|
|
||||||
print_simple_box_start('center', '', '', 20);
|
print_box_start();
|
||||||
|
|
||||||
$vars = get_object_vars($adv);
|
$vars = get_object_vars($adv);
|
||||||
|
|
||||||
if (isset($vars)) {
|
if (isset($vars)) {
|
||||||
foreach ($vars as $key => $value) {
|
foreach ($vars as $key => $value) {
|
||||||
$adv->$key = stripslashes(htmlentities($value));
|
$adv->$key = stripslashes(htmlentities($value));
|
||||||
} //foreach
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<form id="query" method="get" action="query.php">
|
<form id="query" method="get" action="query.php">
|
||||||
<?php if (!$advanced) { ?>
|
<?php
|
||||||
<input type="text" name="query_string" length="50" value="<?php print stripslashes(htmlentities($query_string)) ?>" />
|
if (!$advanced) {
|
||||||
<input type="submit" value="Search" />
|
?>
|
||||||
<a href="query.php?a=1">Advanced search</a> |
|
<input type="text" name="query_string" length="50" value="<?php print stripslashes($query_string) ?>" />
|
||||||
<a href="stats.php">Statistics</a>
|
<input type="submit" value="<?php print_string('search', 'search') ?>" />
|
||||||
<?php } else {
|
<a href="query.php?a=1"><?php print_string('advancedsearch', 'search') ?></a> |
|
||||||
print_simple_box_start('center', '', 'white', 10);
|
<a href="stats.php"><?php print_string('statistics', 'search') ?></a>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print_box_start();
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="a" value="<?php print $advanced; ?>"/>
|
<input type="hidden" name="a" value="<?php print $advanced; ?>"/>
|
||||||
|
|
||||||
<table border="0" cellpadding="3" cellspacing="3">
|
<table border="0" cellpadding="3" cellspacing="3">
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td width="240">These words must appear:</td>
|
<td width="240"><?php print_string('thesewordsmustappear', 'search') ?>:</td>
|
||||||
<td><input type="text" name="mustappear" length="50" value="<?php print $adv->mustappear; ?>" /></td>
|
<td><input type="text" name="mustappear" length="50" value="<?php print $adv->mustappear; ?>" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>These words must not appear:</td>
|
<td><?php print_string('thesewordsmustnotappear', 'search') ?>:</td>
|
||||||
<td><input type="text" name="notappear" length="50" value="<?php print $adv->notappear; ?>" /></td>
|
<td><input type="text" name="notappear" length="50" value="<?php print $adv->notappear; ?>" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>These words help improve rank:</td>
|
<td><?php print_string('thesewordshelpimproverank', 'search') ?>:</td>
|
||||||
<td><input type="text" name="canappear" length="50" value="<?php print $adv->canappear; ?>" /></td>
|
<td><input type="text" name="canappear" length="50" value="<?php print $adv->canappear; ?>" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Which modules to search?:</td>
|
<td><?php print_string('whichmodulestosearch?', 'search') ?>:</td>
|
||||||
<td>
|
<td>
|
||||||
<select name="module">
|
<select name="module">
|
||||||
<?php foreach($module_types as $mod) {
|
<?php
|
||||||
|
foreach($module_types as $mod) {
|
||||||
if ($mod == $adv->module) {
|
if ($mod == $adv->module) {
|
||||||
print "<option value='$mod' selected>$mod</option>\n";
|
if ($mod != 'all'){
|
||||||
} else {
|
print "<option value='$mod' selected=\"selected\">".get_string('modulenameplural', $mod)."</option>\n";
|
||||||
print "<option value='$mod'>$mod</option>\n";
|
}
|
||||||
} //else
|
else{
|
||||||
} ?>
|
print "<option value='$mod' selected=\"selected\">".get_string('all', 'search')."</option>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($mod != 'all'){
|
||||||
|
print "<option value='$mod'>".get_string('modulenameplural', $mod)."</option>\n";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
print "<option value='$mod'>".get_string('all', 'search')."</option>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Words in title:</td>
|
<td><?php print_string('wordsintitle', 'search') ?>:</td>
|
||||||
<td><input type="text" name="title" length="50" value="<?php print $adv->title; ?>" /></td>
|
<td><input type="text" name="title" length="50" value="<?php print $adv->title; ?>" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Author name:</td>
|
<td><?php print_string('authorname', 'search') ?>:</td>
|
||||||
<td><input type="text" name="author" length="50" value="<?php print $adv->author; ?>" /></td>
|
<td><input type="text" name="author" length="50" value="<?php print $adv->author; ?>" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="3" align="center"><br /><input type="submit" value="Search" /></td>
|
<td colspan="3" align="center"><br /><input type="submit" value="<?php print_string('search', 'search') ?>" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="3" align="center">
|
<td colspan="3" align="center">
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
<table border="0" cellpadding="0" cellspacing="0">
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="query.php">Normal search</a> |</td>
|
<td><a href="query.php"><?php print_string('normalsearch', 'search') ?></a> |</td>
|
||||||
<td> <a href="stats.php">Statistics</a></td>
|
<td> <a href="stats.php"><?php print_string('statistics', 'search') ?></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<?php
|
<?php
|
||||||
print_simple_box_end();
|
print_box_end();
|
||||||
} //if
|
}
|
||||||
?>
|
?>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
<?php
|
<?php
|
||||||
|
print_string('searching', 'search') . ': ';
|
||||||
|
|
||||||
print '<div align="center">';
|
if ($sq->is_valid_index()) {
|
||||||
print 'Searching: ';
|
|
||||||
|
|
||||||
if ($sq->is_valid_index()) {
|
|
||||||
//use cached variable to show up-to-date index size (takes deletions into account)
|
//use cached variable to show up-to-date index size (takes deletions into account)
|
||||||
print $CFG->search_index_size;
|
print $CFG->search_index_size;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
print "0";
|
print "0";
|
||||||
} //else
|
}
|
||||||
|
|
||||||
print ' documents.';
|
print ' ';
|
||||||
|
print_string('documents', 'search');
|
||||||
|
print '.';
|
||||||
|
|
||||||
if (!$sq->is_valid_index() and isadmin()) {
|
if (!$sq->is_valid_index() and isadmin()) {
|
||||||
print "<p>Admin: There appears to be no search index. Please <a href='indexersplash.php'>create an index</a>.</p>\n";
|
print '<p>' . get_string('noindexmessage', 'search') . '<a href="indexersplash.php">' . get_string('createanindex', 'search')."</a></p>\n";
|
||||||
} //if
|
}
|
||||||
|
|
||||||
print '</div>';
|
?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
print_box_end();
|
||||||
|
|
||||||
print_simple_box_end();
|
// prints all the results in a box
|
||||||
|
if ($sq->is_valid()) {
|
||||||
if ($sq->is_valid()) {
|
print_box_start();
|
||||||
print_simple_box_start('center', '50%', 'white', 10);
|
|
||||||
|
|
||||||
search_stopwatch();
|
search_stopwatch();
|
||||||
$hit_count = $sq->count();
|
$hit_count = $sq->count();
|
||||||
|
|
||||||
print "<br />";
|
print "<br />";
|
||||||
|
|
||||||
print $hit_count." results returned for '".stripslashes($query_string)."'.";
|
print $hit_count.' '.get_string('resultsreturnedfor', 'search') . " '".stripslashes($query_string)."'.";
|
||||||
print "<br />";
|
print "<br />";
|
||||||
|
|
||||||
if ($hit_count > 0) {
|
if ($hit_count > 0) {
|
||||||
|
@ -264,35 +293,45 @@
|
||||||
$hits = $sq->results();
|
$hits = $sq->results();
|
||||||
|
|
||||||
if ($advanced) {
|
if ($advanced) {
|
||||||
//if in advanced mode, search options are saved in the session, so
|
// if in advanced mode, search options are saved in the session, so
|
||||||
//we can remove the query string var from the page links, and replace
|
// we can remove the query string var from the page links, and replace
|
||||||
//it with a=1 (Advanced = on) instead
|
// it with a=1 (Advanced = on) instead
|
||||||
$page_links = preg_replace("/query_string=[^&]+/", 'a=1', $page_links);
|
$page_links = preg_replace("/query_string=[^&]+/", 'a=1', $page_links);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
print "<ol>";
|
print "<ol>";
|
||||||
|
|
||||||
|
$typestr = get_string('type', 'search');
|
||||||
|
$scorestr = get_string('score', 'search');
|
||||||
|
$authorstr = get_string('author', 'search');
|
||||||
foreach ($hits as $listing) {
|
foreach ($hits as $listing) {
|
||||||
print "<li value='".($listing->number+1)."'><a href='".$listing->url."'>$listing->title</a><br />\n"
|
if ($CFG->unicodedb) $listing->title = mb_convert_encoding($listing->title, 'auto', 'UTF8');
|
||||||
|
$title_post_processing_function = $listing->doctype.'_link_post_processing';
|
||||||
|
require_once "{$CFG->dirroot}/search/documents/{$listing->doctype}_document.php";
|
||||||
|
if (function_exists($title_post_processing_function))
|
||||||
|
$listing->title = $title_post_processing_function($listing->title);
|
||||||
|
print "<li value='".($listing->number+1)."'><a href='".str_replace('DEFAULT_POPUP_SETTINGS', DEFAULT_POPUP_SETTINGS ,$listing->url)."'>$listing->title</a><br />\n"
|
||||||
."<em>".search_shorten_url($listing->url, 70)."</em><br />\n"
|
."<em>".search_shorten_url($listing->url, 70)."</em><br />\n"
|
||||||
."Type: ".$listing->doctype.", score: ".round($listing->score, 3).", author: ".$listing->author."\n"
|
."{$typestr}: ".$listing->doctype.", {$scorestr}: ".round($listing->score, 3).", {$authorstr}: ".$listing->author."\n"
|
||||||
."</li>\n";
|
."</li>\n";
|
||||||
} //for
|
}
|
||||||
|
|
||||||
print "</ol>";
|
print "</ol>";
|
||||||
print $page_links;
|
print $page_links;
|
||||||
} //if
|
}
|
||||||
|
|
||||||
print_simple_box_end();
|
print_box_end();
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
It took <?php search_stopwatch(); ?> to fetch these results.
|
<?php
|
||||||
|
print_string('ittook', 'search');
|
||||||
|
search_stopwatch();
|
||||||
|
print_string('tofetchtheseresults', 'search');
|
||||||
|
?>.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
} //if (sq is valid)
|
}
|
||||||
|
print_box_end();
|
||||||
print_simple_box_end();
|
print_footer();
|
||||||
print_footer();
|
|
||||||
?>
|
?>
|
|
@ -1,22 +1,28 @@
|
||||||
<?php
|
<?php
|
||||||
require_once("$CFG->dirroot/search/Zend/Search/Lucene.php");
|
require_once("{$CFG->dirroot}/search/Zend/Search/Lucene.php");
|
||||||
|
|
||||||
class SearchResult {
|
define('DEFAULT_POPUP_SETTINGS', "\"menubar=0,location=0,scrollbars,resizable,width=600,height=450\"");
|
||||||
public $url,
|
|
||||||
|
/**
|
||||||
|
* a class that represents a single result record of the search engine
|
||||||
|
*/
|
||||||
|
class SearchResult {
|
||||||
|
public $url,
|
||||||
$title,
|
$title,
|
||||||
$doctype,
|
$doctype,
|
||||||
$author,
|
$author,
|
||||||
$score,
|
$score,
|
||||||
$number;
|
$number;
|
||||||
} //SearchResult
|
} //SearchResult
|
||||||
|
|
||||||
|
|
||||||
//split this into Cache class and extend to SearchCache?
|
//split this into Cache class and extend to SearchCache?
|
||||||
class SearchCache {
|
class SearchCache {
|
||||||
private $mode,
|
private $mode,
|
||||||
$valid;
|
$valid;
|
||||||
|
|
||||||
public function __construct($mode='session') {
|
// foresees other caching locations
|
||||||
|
public function __construct($mode = 'session') {
|
||||||
$accepted_modes = array('session');
|
$accepted_modes = array('session');
|
||||||
|
|
||||||
if (in_array($mode, $accepted_modes)) {
|
if (in_array($mode, $accepted_modes)) {
|
||||||
|
@ -28,11 +34,19 @@
|
||||||
$this->valid = true;
|
$this->valid = true;
|
||||||
} //constructor
|
} //constructor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the search cache status
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
public function can_cache() {
|
public function can_cache() {
|
||||||
return $this->valid;
|
return $this->valid;
|
||||||
} //can_cache
|
} //can_cache
|
||||||
|
|
||||||
public function cache($id=false, $object=false) {
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function cache($id = false, $object = false) {
|
||||||
//see if there was a previous query
|
//see if there was a previous query
|
||||||
$last_term = $this->fetch('search_last_term');
|
$last_term = $this->fetch('search_last_term');
|
||||||
|
|
||||||
|
@ -47,11 +61,17 @@
|
||||||
$this->store($id, $object);
|
$this->store($id, $object);
|
||||||
return true;
|
return true;
|
||||||
//otherwise return the stored results
|
//otherwise return the stored results
|
||||||
} else if ($id and $this->exists($id)) {
|
}
|
||||||
|
else if ($id and $this->exists($id)) {
|
||||||
return $this->fetch($id);
|
return $this->fetch($id);
|
||||||
} //else
|
} //else
|
||||||
} //cache
|
} //cache
|
||||||
|
|
||||||
|
/**
|
||||||
|
* do key exist in cache ?
|
||||||
|
* @param id the object key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
private function exists($id) {
|
private function exists($id) {
|
||||||
switch ($this->mode) {
|
switch ($this->mode) {
|
||||||
case 'session' :
|
case 'session' :
|
||||||
|
@ -59,6 +79,11 @@
|
||||||
} //switch
|
} //switch
|
||||||
} //exists
|
} //exists
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clears a cached object in cache
|
||||||
|
* @param the object key to clear
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
private function clear($id) {
|
private function clear($id) {
|
||||||
switch ($this->mode) {
|
switch ($this->mode) {
|
||||||
case 'session' :
|
case 'session' :
|
||||||
|
@ -68,6 +93,11 @@
|
||||||
} //switch
|
} //switch
|
||||||
} //clear
|
} //clear
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fetches a cached object
|
||||||
|
* @param id the object identifier
|
||||||
|
* @return the object cached
|
||||||
|
*/
|
||||||
private function fetch($id) {
|
private function fetch($id) {
|
||||||
switch ($this->mode) {
|
switch ($this->mode) {
|
||||||
case 'session' :
|
case 'session' :
|
||||||
|
@ -75,6 +105,12 @@
|
||||||
} //switch
|
} //switch
|
||||||
} //fetch
|
} //fetch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* put an object in cache
|
||||||
|
* @param id the key for that object
|
||||||
|
* @param object the object to cache as a serialized value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
private function store($id, $object) {
|
private function store($id, $object) {
|
||||||
switch ($this->mode) {
|
switch ($this->mode) {
|
||||||
case 'session' :
|
case 'session' :
|
||||||
|
@ -82,10 +118,13 @@
|
||||||
return;
|
return;
|
||||||
} //switch
|
} //switch
|
||||||
} //store
|
} //store
|
||||||
} //SearchCache
|
} //SearchCache
|
||||||
|
|
||||||
|
/**
|
||||||
class SearchQuery {
|
* Represents a single query with results
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class SearchQuery {
|
||||||
private $index,
|
private $index,
|
||||||
$term,
|
$term,
|
||||||
$pagenumber,
|
$pagenumber,
|
||||||
|
@ -96,7 +135,11 @@
|
||||||
$results_per_page,
|
$results_per_page,
|
||||||
$total_results;
|
$total_results;
|
||||||
|
|
||||||
public function __construct($term='', $page=1, $results_per_page=10, $cache=false) {
|
/**
|
||||||
|
* constructor records query parameters
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct($term = '', $page = 1, $results_per_page = 10, $cache = false) {
|
||||||
global $CFG;
|
global $CFG;
|
||||||
|
|
||||||
$this->term = $term;
|
$this->term = $term;
|
||||||
|
@ -122,28 +165,43 @@
|
||||||
} //else
|
} //else
|
||||||
} //constructor
|
} //constructor
|
||||||
|
|
||||||
public function set_query($term='') {
|
/**
|
||||||
|
* determines state of query object depending on query entry and
|
||||||
|
* tries to lauch search if all is OK
|
||||||
|
* @return void (this is only a state changing trigger).
|
||||||
|
*/
|
||||||
|
public function set_query($term = '') {
|
||||||
if (!empty($term)) {
|
if (!empty($term)) {
|
||||||
$this->term = $term;
|
$this->term = $term;
|
||||||
} //if
|
} //if
|
||||||
|
|
||||||
if (empty($this->term)) {
|
if (empty($this->term)) {
|
||||||
$this->validquery = false;
|
$this->validquery = false;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$this->validquery = true;
|
$this->validquery = true;
|
||||||
} //else
|
} //else
|
||||||
|
|
||||||
if ($this->validquery and $this->validindex) {
|
if ($this->validquery and $this->validindex) {
|
||||||
$this->results = $this->get_results();
|
$this->results = $this->get_results();
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$this->results = array();
|
$this->results = array();
|
||||||
} //else
|
} //else
|
||||||
} //set_query
|
} //set_query
|
||||||
|
|
||||||
|
/**
|
||||||
|
* accessor to the result table.
|
||||||
|
* @return an array of result records
|
||||||
|
*/
|
||||||
public function results() {
|
public function results() {
|
||||||
return $this->results;
|
return $this->results;
|
||||||
} //results
|
} //results
|
||||||
|
|
||||||
|
/**
|
||||||
|
* do the effective collection of results
|
||||||
|
*
|
||||||
|
*/
|
||||||
private function process_results($all=false) {
|
private function process_results($all=false) {
|
||||||
global $USER;
|
global $USER;
|
||||||
|
|
||||||
|
@ -166,8 +224,9 @@
|
||||||
if (!$all) {
|
if (!$all) {
|
||||||
if ($hitcount < $this->results_per_page) {
|
if ($hitcount < $this->results_per_page) {
|
||||||
$this->pagenumber = 1;
|
$this->pagenumber = 1;
|
||||||
} else if ($this->pagenumber > $totalpages) {
|
}
|
||||||
$this->pagenumber =$totalpages;
|
else if ($this->pagenumber > $totalpages) {
|
||||||
|
$this->pagenumber = $totalpages;
|
||||||
} //if
|
} //if
|
||||||
|
|
||||||
$start = ($this->pagenumber - 1) * $this->results_per_page;
|
$start = ($this->pagenumber - 1) * $this->results_per_page;
|
||||||
|
@ -176,7 +235,8 @@
|
||||||
if ($end > $hitcount) {
|
if ($end > $hitcount) {
|
||||||
$end = $hitcount;
|
$end = $hitcount;
|
||||||
} //if
|
} //if
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$start = 0;
|
$start = 0;
|
||||||
$end = $hitcount;
|
$end = $hitcount;
|
||||||
} //else
|
} //else
|
||||||
|
@ -188,7 +248,7 @@
|
||||||
$hit = $hits[$i];
|
$hit = $hits[$i];
|
||||||
|
|
||||||
//check permissions on each result
|
//check permissions on each result
|
||||||
if ($this->can_display($USER, $hit->id, $hit->doctype, $hit->course_id, $hit->group_id)) {
|
if ($this->can_display($USER, $hit->docid, $hit->doctype, $hit->course_id, $hit->group_id, $hit->path, $hit->itemtype, $hit->context_id )) {
|
||||||
$resultdoc->number = $i;
|
$resultdoc->number = $i;
|
||||||
$resultdoc->url = $hit->url;
|
$resultdoc->url = $hit->url;
|
||||||
$resultdoc->title = $hit->title;
|
$resultdoc->title = $hit->title;
|
||||||
|
@ -199,11 +259,19 @@
|
||||||
//and store it
|
//and store it
|
||||||
$resultdocs[] = clone($resultdoc);
|
$resultdocs[] = clone($resultdoc);
|
||||||
} //if
|
} //if
|
||||||
|
else{
|
||||||
|
// lowers total_results one unit
|
||||||
|
$this->total_results--;
|
||||||
|
}
|
||||||
} //foreach
|
} //foreach
|
||||||
|
|
||||||
return $resultdocs;
|
return $resultdocs;
|
||||||
} //process_results
|
} //process_results
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get results of a search query using a caching strategy if available
|
||||||
|
* @return the result documents as an array of search objects
|
||||||
|
*/
|
||||||
private function get_results() {
|
private function get_results() {
|
||||||
$cache = new SearchCache();
|
$cache = new SearchCache();
|
||||||
|
|
||||||
|
@ -213,11 +281,13 @@
|
||||||
//cache the results so we don't have to compute this on every page-load
|
//cache the results so we don't have to compute this on every page-load
|
||||||
$cache->cache($this->term, $resultdocs);
|
$cache->cache($this->term, $resultdocs);
|
||||||
//print "Using new results.";
|
//print "Using new results.";
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
//There was something in the cache, so we're using that to save time
|
//There was something in the cache, so we're using that to save time
|
||||||
//print "Using cached results.";
|
//print "Using cached results.";
|
||||||
} //else
|
} //else
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
//no caching :(
|
//no caching :(
|
||||||
//print "Caching disabled!";
|
//print "Caching disabled!";
|
||||||
$resultdocs = $this->process_results();
|
$resultdocs = $this->process_results();
|
||||||
|
@ -226,36 +296,40 @@
|
||||||
return $resultdocs;
|
return $resultdocs;
|
||||||
} //get_results
|
} //get_results
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructs the results paging links on results.
|
||||||
|
* @return string the results paging links
|
||||||
|
*/
|
||||||
public function page_numbers() {
|
public function page_numbers() {
|
||||||
$pages = $this->total_pages();
|
$pages = $this->total_pages();
|
||||||
$query = htmlentities($this->term);
|
$query = htmlentities($this->term);
|
||||||
$page = $this->pagenumber;
|
$page = $this->pagenumber;
|
||||||
$next = "Next";
|
$next = get_string('next', 'search');
|
||||||
$back = "Back";
|
$back = get_string('back', 'search');
|
||||||
|
|
||||||
$ret = "<div align='center' id='search_page_links'>";
|
$ret = "<div align='center' id='search_page_links'>";
|
||||||
|
|
||||||
//Back is disabled if we're on page 1
|
//Back is disabled if we're on page 1
|
||||||
if ($page > 1) {
|
if ($page > 1) {
|
||||||
$ret .= "<a href='query.php?query_string=$query&page=".($page-1)."'>< $back</a> ";
|
$ret .= "<a href='query.php?query_string={$query}&page=".($page-1)."'>< {$back}</a> ";
|
||||||
} else {
|
} else {
|
||||||
$ret .= "< $back ";
|
$ret .= "< {$back} ";
|
||||||
} //else
|
} //else
|
||||||
|
|
||||||
//don't <a href> the current page
|
//don't <a href> the current page
|
||||||
for ($i = 1; $i <= $pages; $i++) {
|
for ($i = 1; $i <= $pages; $i++) {
|
||||||
if ($page == $i) {
|
if ($page == $i) {
|
||||||
$ret .= "[$i] ";
|
$ret .= "($i) ";
|
||||||
} else {
|
} else {
|
||||||
$ret .= "<a href='query.php?query_string=$query&page=$i'>$i</a> ";
|
$ret .= "<a href='query.php?query_string={$query}&page={$i}'>{$i}</a> ";
|
||||||
} //else
|
} //else
|
||||||
} //for
|
} //for
|
||||||
|
|
||||||
//Next disabled if we're on the last page
|
//Next disabled if we're on the last page
|
||||||
if ($page < $pages) {
|
if ($page < $pages) {
|
||||||
$ret .= "<a href='query.php?query_string=$query&page=".($page+1)."'>$next ></a> ";
|
$ret .= "<a href='query.php?query_string={$query}&page=".($page+1)."'>{$next} ></a> ";
|
||||||
} else {
|
} else {
|
||||||
$ret .= "$next > ";
|
$ret .= "{$next} > ";
|
||||||
} //else
|
} //else
|
||||||
|
|
||||||
$ret .= "</div>";
|
$ret .= "</div>";
|
||||||
|
@ -274,13 +348,68 @@
|
||||||
return $ret;
|
return $ret;
|
||||||
} //page_numbers
|
} //page_numbers
|
||||||
|
|
||||||
//can the user see this result?
|
/**
|
||||||
private function can_display(&$user, $this_id, $doctype, $course_id, $group_id) {
|
* can the user see this result ?
|
||||||
//this function should return true/false depending on
|
* @param user a reference upon the user to be checked for access
|
||||||
//whether or not a user can see this resource
|
* @param this_id the item identifier
|
||||||
//..
|
* @param doctype the search document type. MAtches the module or block or
|
||||||
//if one of you nice moodlers see this, feel free to
|
* extra search source definition
|
||||||
//implement it for me .. :-P
|
* @param course_id the course reference of the searched result
|
||||||
|
* @param group_id the group identity attached to the found resource
|
||||||
|
* @param path the path that routes to the local lib.php of the searched
|
||||||
|
* surrounding object fot that document
|
||||||
|
* @param item_type a subclassing information for complex module data models
|
||||||
|
* // TODO reorder parameters more consistently
|
||||||
|
*/
|
||||||
|
private function can_display(&$user, $this_id, $doctype, $course_id, $group_id, $path, $item_type, $context_id) {
|
||||||
|
global $CFG;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* course related checks
|
||||||
|
*/
|
||||||
|
// admins can see everything, anyway.
|
||||||
|
if (isadmin()){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first check course compatibility against user : enrolled users to that course can see.
|
||||||
|
$myCourses = get_my_courses($user->id);
|
||||||
|
$unenroled = !in_array($course_id, array_keys($myCourses));
|
||||||
|
|
||||||
|
// if guests are allowed, logged guest can see
|
||||||
|
$isallowedguest = (isguest()) ? get_field('course', 'guest', 'id', $course_id) : false ;
|
||||||
|
|
||||||
|
if ($unenroled && !$isallowedguest){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if user is enrolled or is allowed user and course is hidden, can he see it ?
|
||||||
|
$visibility = get_field('course', 'visible', 'id', $course_id);
|
||||||
|
if ($visibility <= 0){
|
||||||
|
if (!has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prerecorded capabilities
|
||||||
|
*/
|
||||||
|
// get context caching information and tries to discard unwanted records here
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* final checks
|
||||||
|
*/
|
||||||
|
// then give back indexing data to the module for local check
|
||||||
|
include_once "{$CFG->dirroot}/search/documents/{$doctype}_document.php";
|
||||||
|
$access_check_function = "{$doctype}_check_text_access";
|
||||||
|
|
||||||
|
if (function_exists($access_check_function)){
|
||||||
|
$modulecheck = $access_check_function($path, $item_type, $this_id, $user, $group_id, $context_id);
|
||||||
|
// echo "module said $modulecheck for item $doctype/$item_type/$this_id";
|
||||||
|
return($modulecheck);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} //can_display
|
} //can_display
|
||||||
|
|
||||||
|
|
175
search/stats.php
175
search/stats.php
|
@ -1,51 +1,75 @@
|
||||||
<?php
|
<?php
|
||||||
/* Prints some basic statistics about the current index.
|
/**
|
||||||
*
|
* Global Search Engine for Moodle
|
||||||
* Does some diagnostics if you are logged in as an administrator.
|
* Michael Champanis (mchampan) [cynnical@gmail.com]
|
||||||
* */
|
* review 1.8+ : Valery Fremaux [valery.fremaux@club-internet.fr]
|
||||||
|
* 2007/08/02
|
||||||
|
*
|
||||||
|
* Prints some basic statistics about the current index.
|
||||||
|
* Does some diagnostics if you are logged in as an administrator.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
require_once('../config.php');
|
require_once('../config.php');
|
||||||
require_once("$CFG->dirroot/search/lib.php");
|
require_once("{$CFG->dirroot}/search/lib.php");
|
||||||
|
|
||||||
if ($CFG->forcelogin) {
|
if ($CFG->forcelogin) {
|
||||||
require_login();
|
require_login();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($CFG->enableglobalsearch)) {
|
if (empty($CFG->enableglobalsearch)) {
|
||||||
error('Global searching is not enabled.');
|
error(get_string('globalsearchdisabled', 'search'));
|
||||||
}
|
}
|
||||||
|
|
||||||
//check for php5, but don't die yet
|
//check for php5, but don't die yet
|
||||||
if ($check = search_check_php5()) {
|
if ($check = search_check_php5()) {
|
||||||
require_once("$CFG->dirroot/search/indexlib.php");
|
require_once("{$CFG->dirroot}/search/indexlib.php");
|
||||||
|
|
||||||
$indexinfo = new IndexInfo();
|
$indexinfo = new IndexInfo();
|
||||||
} //if
|
}
|
||||||
|
|
||||||
if (!$site = get_site()) {
|
if (!$site = get_site()) {
|
||||||
redirect("index.php");
|
redirect("index.php");
|
||||||
} //if
|
}
|
||||||
|
|
||||||
$strsearch = "Search"; //get_string();
|
$strsearch = get_string('search', 'search');
|
||||||
$strquery = "Search statistics"; //get_string();
|
$strquery = get_string('statistics', 'search');
|
||||||
|
|
||||||
print_header("$site->shortname: $strsearch: $strquery", "$site->fullname",
|
print_header("$site->shortname: $strsearch: $strquery", "$site->fullname",
|
||||||
"<a href=\"index.php\">$strsearch</a> -> $strquery");
|
"<a href=\"index.php\">$strsearch</a> -> $strquery");
|
||||||
|
|
||||||
//keep things pretty, even if php5 isn't available
|
//keep things pretty, even if php5 isn't available
|
||||||
if (!$check) {
|
if (!$check) {
|
||||||
print_heading(search_check_php5(true));
|
print_heading(search_check_php5(true));
|
||||||
print_footer();
|
print_footer();
|
||||||
exit(0);
|
exit(0);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
print_simple_box_start('center', '100%', '', 20);
|
print_box_start();
|
||||||
print_heading($strquery);
|
print_heading($strquery);
|
||||||
|
|
||||||
print_simple_box_start('center', '', '', 20);
|
print_box_start();
|
||||||
|
|
||||||
|
$databasestr = get_string('database', 'search');
|
||||||
|
$documentsinindexstr = get_string('documentsinindex', 'search');
|
||||||
|
$deletionsinindexstr = get_string('deletionsinindex', 'search');
|
||||||
|
$documentsindatabasestr = get_string('documentsindatabase', 'search');
|
||||||
|
$databasestatestr = get_string('databasestate', 'search');
|
||||||
|
|
||||||
|
//this table is only for admins, shows index directory size and location
|
||||||
|
if (isadmin()) {
|
||||||
|
$datadirectorystr = get_string('datadirectory', 'search');
|
||||||
|
$inindexdirectorystr = get_string('filesinindexdirectory', 'search');
|
||||||
|
$totalsizestr = get_string('totalsize', 'search');
|
||||||
|
$errorsstr = get_string('errors', 'search');
|
||||||
|
$solutionsstr = get_string('solutions', 'search');
|
||||||
|
$checkdirstr = get_string('checkdir', 'search');
|
||||||
|
$checkdbstr = get_string('checkdb', 'search');
|
||||||
|
$checkdiradvicestr = get_string('checkdiradvice', 'search');
|
||||||
|
$checkdbadvicestr = get_string('checkdbadvice', 'search');
|
||||||
|
$runindexerteststr = get_string('runindexertest', 'search');
|
||||||
|
$runindexerstr = get_string('runindexer', 'search');
|
||||||
|
|
||||||
//this table is only for admins, shows index directory size and location
|
|
||||||
if (isadmin()) {
|
|
||||||
$admin_table->tablealign = "center";
|
$admin_table->tablealign = "center";
|
||||||
$admin_table->align = array ("right", "left");
|
$admin_table->align = array ("right", "left");
|
||||||
$admin_table->wrap = array ("nowrap", "nowrap");
|
$admin_table->wrap = array ("nowrap", "nowrap");
|
||||||
|
@ -53,72 +77,73 @@
|
||||||
$admin_table->cellspacing = 0;
|
$admin_table->cellspacing = 0;
|
||||||
$admin_table->width = '500';
|
$admin_table->width = '500';
|
||||||
|
|
||||||
$admin_table->data[] = array('<strong>Data directory</strong>', '<em><strong>'.$indexinfo->path.'</strong></em>');
|
$admin_table->data[] = array("<strong>{$datadirectorystr}</strong>", '<em><strong>'.$indexinfo->path.'</strong></em>');
|
||||||
$admin_table->data[] = array('Files in index directory', $indexinfo->filecount);
|
$admin_table->data[] = array($inindexdirectorystr, $indexinfo->filecount);
|
||||||
$admin_table->data[] = array('Total size', $indexinfo->size);
|
$admin_table->data[] = array($totalsizestr, $indexinfo->size);
|
||||||
|
|
||||||
if ($indexinfo->time > 0) {
|
if ($indexinfo->time > 0) {
|
||||||
$admin_table->data[] = array('Created on', date('r', $indexinfo->time));
|
$admin_table->data[] = array(get_string('createdon', 'search'), date('r', $indexinfo->time));
|
||||||
} else {
|
}
|
||||||
$admin_table->data[] = array('Created on', '-');
|
else {
|
||||||
} //else
|
$admin_table->data[] = array(get_string('createdon', 'search'), '-');
|
||||||
|
}
|
||||||
|
|
||||||
if (!$indexinfo->valid($errors)) {
|
if (!$indexinfo->valid($errors)) {
|
||||||
$admin_table->data[] = array('<strong>Errors</strong>', ' ');
|
$admin_table->data[] = array("<strong>{$errorsstr}</strong>", ' ');
|
||||||
|
foreach ($errors as $key => $value) {
|
||||||
foreach ($errors as $key=>$value) {
|
|
||||||
$admin_table->data[] = array($key.' ... ', $value);
|
$admin_table->data[] = array($key.' ... ', $value);
|
||||||
} //foreach
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$admin_table->data[] = array('<strong>Solutions</strong>', ' ');
|
print_table($admin_table);
|
||||||
|
print_spacer(20);
|
||||||
|
print_heading($solutionsstr);
|
||||||
|
|
||||||
|
unset($admin_table->data);
|
||||||
if (isset($errors['dir'])) {
|
if (isset($errors['dir'])) {
|
||||||
$admin_table->data[] = array('Check dir', 'Ensure the data directory exists and is writable.');
|
$admin_table->data[] = array($checkdirstr, $checkdiradvicestr);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
if (isset($errors['db'])) {
|
if (isset($errors['db'])) {
|
||||||
$admin_table->data[] = array('Check DB', 'Check your database for any problems.');
|
$admin_table->data[] = array($checkdbstr, $checkdbadvicestr);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
$admin_table->data[] = array('Run indexer test', '<a href=\'tests/index.php\'>tests/index.php</a>');
|
$admin_table->data[] = array($runindexerteststr, '<a href="tests/index.php" target="_blank">tests/index.php</a>');
|
||||||
$admin_table->data[] = array('Run indexer', '<a href=\'indexersplash.php\'>indexersplash.php</a>');
|
$admin_table->data[] = array($runindexerstr, '<a href="indexersplash.php" target="_blank">indexersplash.php</a>');
|
||||||
} //if
|
|
||||||
} //if
|
|
||||||
|
|
||||||
//this is the standard summary table for normal users, shows document counts
|
print_table($admin_table);
|
||||||
$table->tablealign = "center";
|
print_spacer(20);
|
||||||
$table->align = array ("right", "left");
|
}
|
||||||
$table->wrap = array ("nowrap", "nowrap");
|
|
||||||
$table->cellpadding = 5;
|
|
||||||
$table->cellspacing = 0;
|
|
||||||
$table->width = '500';
|
|
||||||
|
|
||||||
$table->data[] = array('<strong>Database</strong>', '<em><strong>search_documents<strong></em>');
|
//this is the standard summary table for normal users, shows document counts
|
||||||
|
$table->tablealign = "center";
|
||||||
|
$table->align = array ("right", "left");
|
||||||
|
$table->wrap = array ("nowrap", "nowrap");
|
||||||
|
$table->cellpadding = 5;
|
||||||
|
$table->cellspacing = 0;
|
||||||
|
$table->width = '500';
|
||||||
|
|
||||||
//add extra fields if we're admin
|
$table->data[] = array("<strong>{$databasestr}</strong>", "<em><strong>{$CFG->prefix}search_documents</strong></em>");
|
||||||
if (isadmin()) {
|
|
||||||
|
//add extra fields if we're admin
|
||||||
|
if (isadmin()) {
|
||||||
//don't want to confuse users if the two totals don't match (hint: they should)
|
//don't want to confuse users if the two totals don't match (hint: they should)
|
||||||
$table->data[] = array('Documents in index', $indexinfo->indexcount);
|
$table->data[] = array($documentsinindexstr, $indexinfo->indexcount);
|
||||||
|
|
||||||
//*cough* they should match if deletions were actually removed from the index,
|
//*cough* they should match if deletions were actually removed from the index,
|
||||||
//as it turns out, they're only marked as deleted and not returned in search results
|
//as it turns out, they're only marked as deleted and not returned in search results
|
||||||
$table->data[] = array('Deletions in index', (int)$indexinfo->indexcount - (int)$indexinfo->dbcount);
|
$table->data[] = array($deletionsinindexstr, (int)$indexinfo->indexcount - (int)$indexinfo->dbcount);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
$table->data[] = array('Documents in database', $indexinfo->dbcount);
|
$table->data[] = array($documentsindatabasestr, $indexinfo->dbcount);
|
||||||
|
|
||||||
foreach($indexinfo->types as $key => $value) {
|
foreach($indexinfo->types as $key => $value) {
|
||||||
$table->data[] = array("'$key' documents", $value);
|
$table->data[] = array(get_string('documentsfor', 'search') . " '".get_string('modulenameplural', $key)."'", $value);
|
||||||
} //foreach
|
}
|
||||||
|
|
||||||
if (isadmin()) {
|
print_heading($databasestatestr);
|
||||||
print_table($admin_table);
|
print_table($table);
|
||||||
print_spacer(20);
|
|
||||||
} //if
|
|
||||||
|
|
||||||
print_table($table);
|
print_box_end();
|
||||||
|
print_box_end();
|
||||||
print_simple_box_end();
|
print_footer();
|
||||||
print_simple_box_end();
|
|
||||||
print_footer();
|
|
||||||
?>
|
?>
|
|
@ -1,36 +1,47 @@
|
||||||
<?php
|
<?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
|
||||||
|
*
|
||||||
|
* Index asynchronous updator
|
||||||
|
*
|
||||||
|
* Major chages in this review is passing the xxxx_db_names return to
|
||||||
|
* multiple arity to handle multiple document types modules
|
||||||
|
*/
|
||||||
|
|
||||||
require_once('../config.php');
|
require_once('../config.php');
|
||||||
require_once("$CFG->dirroot/search/lib.php");
|
require_once("$CFG->dirroot/search/lib.php");
|
||||||
|
|
||||||
require_login();
|
require_login();
|
||||||
|
|
||||||
if (empty($CFG->enableglobalsearch)) {
|
if (empty($CFG->enableglobalsearch)) {
|
||||||
error('Global searching is not enabled.');
|
error(get_string('globalsearchdisabled', 'search'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isadmin()) {
|
if (!isadmin()) {
|
||||||
error("You need to be an admin user to use this page.", "$CFG->wwwroot/login/index.php");
|
error(get_string('beadmin', 'search'), "$CFG->wwwroot/login/index.php");
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//check for php5 (lib.php)
|
//check for php5 (lib.php)
|
||||||
if (!search_check_php5()) {
|
if (!search_check_php5()) {
|
||||||
$phpversion = phpversion();
|
$phpversion = phpversion();
|
||||||
mtrace("Sorry, global search requires PHP 5.0.0 or later (currently using version $phpversion)");
|
mtrace("Sorry, global search requires PHP 5.0.0 or later (currently using version $phpversion)");
|
||||||
exit(0);
|
exit(0);
|
||||||
} //if
|
}
|
||||||
|
|
||||||
require_once("$CFG->dirroot/search/indexlib.php");
|
require_once("$CFG->dirroot/search/indexlib.php");
|
||||||
|
|
||||||
$index = new Zend_Search_Lucene(SEARCH_INDEX_PATH);
|
$index = new Zend_Search_Lucene(SEARCH_INDEX_PATH);
|
||||||
$dbcontrol = new IndexDBControl();
|
$dbcontrol = new IndexDBControl();
|
||||||
$update_count = 0;
|
$update_count = 0;
|
||||||
|
$indexdate = $CFG->search_indexer_update_date;
|
||||||
|
$startupdatedate = time();
|
||||||
|
|
||||||
$indexdate = $CFG->search_indexer_run_date;
|
mtrace("<pre>Starting index update (updates)...\n");
|
||||||
|
|
||||||
mtrace("<pre>Starting index update (updates)...\n");
|
if ($mods = get_records_select('modules')) {
|
||||||
|
|
||||||
if ($mods = get_records_select('modules')) {
|
|
||||||
$mods = array_merge($mods, search_get_additional_modules());
|
$mods = array_merge($mods, search_get_additional_modules());
|
||||||
|
|
||||||
foreach ($mods as $mod) {
|
foreach ($mods as $mod) {
|
||||||
|
@ -43,63 +54,90 @@
|
||||||
if (file_exists($class_file)) {
|
if (file_exists($class_file)) {
|
||||||
require_once($class_file);
|
require_once($class_file);
|
||||||
|
|
||||||
|
//if both required functions exist
|
||||||
if (function_exists($delete_function) and function_exists($db_names_function) and function_exists($get_document_function)) {
|
if (function_exists($delete_function) and function_exists($db_names_function) and function_exists($get_document_function)) {
|
||||||
mtrace("Checking $mod->name module for updates.");
|
mtrace("Checking $mod->name module for updates.");
|
||||||
$values = $db_names_function();
|
$valuesArray = $db_names_function();
|
||||||
|
if ($valuesArray){
|
||||||
|
foreach($valuesArray as $values){
|
||||||
|
|
||||||
|
$where = (isset($values[5])) ? 'AND ('.$values[5].')' : '';
|
||||||
|
$itemtypes = ($values[4] != '*') ? " AND itemtype = '{$values[4]}' " : '' ;
|
||||||
|
|
||||||
//TODO: check 'in' syntax with other RDBMS' (add and update.php as well)
|
//TODO: check 'in' syntax with other RDBMS' (add and update.php as well)
|
||||||
$sql = "select id, ".$values[0]." as docid from ".$values[1].
|
$table = SEARCH_DATABASE_TABLE;
|
||||||
" where ".$values[3]." > $indexdate".
|
$query = "
|
||||||
" and id in (select docid from ".SEARCH_DATABASE_TABLE.")";
|
SELECT
|
||||||
|
docid,
|
||||||
$records = get_records_sql($sql);
|
itemtype
|
||||||
|
FROM
|
||||||
|
{$CFG->prefix}{$table}
|
||||||
|
WHERE
|
||||||
|
doctype = '{$mod->name}'
|
||||||
|
$itemtypes
|
||||||
|
";
|
||||||
|
$docIds = get_records_sql_menu($query);
|
||||||
|
$docIdList = ($docIds) ? implode("','", array_keys($docIds)) : '' ;
|
||||||
|
|
||||||
|
$query = "
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
{$values[0]} as docid
|
||||||
|
FROM
|
||||||
|
{$CFG->prefix}{$values[1]}
|
||||||
|
WHERE
|
||||||
|
{$values[3]} > {$indexdate} AND
|
||||||
|
id IN ('{$docIdList}')
|
||||||
|
$where
|
||||||
|
";
|
||||||
|
$records = get_records_sql($query);
|
||||||
if (is_array($records)) {
|
if (is_array($records)) {
|
||||||
foreach($records as $record) {
|
foreach($records as $record) {
|
||||||
$updates[] = $delete_function($record->docid);
|
$updates[] = $delete_function($record->docid, $docIds[$record->docid]);
|
||||||
} //foreach
|
}
|
||||||
} //if
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($updates as $update) {
|
foreach ($updates as $update) {
|
||||||
++$update_count;
|
++$update_count;
|
||||||
|
|
||||||
//delete old document
|
//delete old document
|
||||||
$doc = $index->find("+docid:$update +doctype:$mod->name");
|
$doc = $index->find("+docid:{$update->id} +doctype:{$mod->name} +itemtype:{$update->itemtype}");
|
||||||
|
|
||||||
//get the record, should only be one
|
//get the record, should only be one
|
||||||
foreach ($doc as $thisdoc) {
|
foreach ($doc as $thisdoc) {
|
||||||
mtrace(" Delete: $thisdoc->title (database id = $thisdoc->dbid, index id = $thisdoc->id, moodle instance id = $thisdoc->docid)");
|
mtrace(" Delete: $thisdoc->title (database id = $thisdoc->dbid, index id = $thisdoc->id, moodle instance id = $thisdoc->docid)");
|
||||||
|
|
||||||
$dbcontrol->delDocument($thisdoc);
|
$dbcontrol->delDocument($thisdoc);
|
||||||
$index->delete($thisdoc->id);
|
$index->delete($thisdoc->id);
|
||||||
} //foreach
|
}
|
||||||
|
|
||||||
//add new modified document back into index
|
//add new modified document back into index
|
||||||
$add = $get_document_function($update);
|
$add = $get_document_function($update->id, $update->itemtype);
|
||||||
|
|
||||||
//object to insert into db
|
//object to insert into db
|
||||||
$dbid = $dbcontrol->addDocument($add);
|
$dbid = $dbcontrol->addDocument($add);
|
||||||
|
|
||||||
//synchronise db with index
|
//synchronise db with index
|
||||||
$add->addField(Zend_Search_Lucene_Field::Keyword('dbid', $dbid));
|
$add->addField(Zend_Search_Lucene_Field::Keyword('dbid', $dbid));
|
||||||
|
|
||||||
mtrace(" Add: $add->title (database id = $add->dbid, moodle instance id = $add->docid)");
|
mtrace(" Add: $add->title (database id = $add->dbid, moodle instance id = $add->docid)");
|
||||||
|
|
||||||
$index->addDocument($add);
|
$index->addDocument($add);
|
||||||
} //foreach
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
mtrace("No types to update.\n");
|
||||||
|
}
|
||||||
mtrace("Finished $mod->name.\n");
|
mtrace("Finished $mod->name.\n");
|
||||||
} //if
|
}
|
||||||
} //if
|
}
|
||||||
} //foreach
|
}
|
||||||
} //if
|
}
|
||||||
|
|
||||||
//commit changes
|
//commit changes
|
||||||
$index->commit();
|
$index->commit();
|
||||||
|
|
||||||
//update index date
|
//update index date
|
||||||
set_config("search_indexer_run_date", time());
|
set_config("search_indexer_update_date", $startupdatedate);
|
||||||
|
|
||||||
mtrace("Finished $update_count updates.</pre>");
|
mtrace("Finished $update_count updates.</pre>");
|
||||||
|
|
||||||
?>
|
?>
|
Loading…
Add table
Add a link
Reference in a new issue