Subversion Repositories Sites.tela-botanica.org

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2005                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


//
// Ce fichier ne sera execute qu'une fois
if (defined("_ECRIRE_INC_INDEX")) return;
define("_ECRIRE_INC_INDEX", "1");

function separateurs_indexation($requete = false) {
        // Merci a Herve Lefebvre pour son apport sur cette fonction
        $liste = "],:;*\"!\r\n\t\\/)}{[|@<>$%";

        // pour autoriser les recherches en vietnamien,
        // ne pas eliminer les accents de translitteration
        if (!$requete)
                $liste .= "'`?\~.^+(-";

        // windowzeries iso-8859-1
        $charset = lire_meta('charset');
        if ($charset == 'iso-8859-1')
                $liste .= chr(187).chr(171).chr(133).chr(145).chr(146).chr(180).chr(147).chr(148);

        return $liste;
}

function nettoyer_chaine_indexation($texte) {
        global $translitteration_complexe;
        include_ecrire("inc_charsets.php3");

        // translitteration complexe (vietnamien, allemand)
        if ($translitteration_complexe) {
                $texte_c = translitteration_complexe ($texte);
                $texte_c = " ".strtr($texte_c, "'`?~.^+(-", "123456789");
        }

        $texte = translitteration($texte).$texte_c;

        return $texte;
}

function indexer_chaine($texte, $val = 1, $min_long = 3) {
        global $index, $mots, $translitteration_complexe;

        // Nettoyer les tags, entites HTML, signes diacritiques...
        $texte = ' '.ereg_replace("<[^>]*>"," ",$texte).' ';
        $texte = nettoyer_chaine_indexation($texte);

        // Nettoyer les caracteres non-alphanumeriques
        $regs = separateurs_indexation();
        $texte = strtr($texte, $regs, ereg_replace('.', ' ', $regs));

        // Cas particulier : sigles d'au moins deux lettres
        $texte = ereg_replace(" ([A-Z][0-9A-Z]{1,".($min_long - 1)."}) ", ' \\1___ ', $texte);
        $texte = strtolower($texte);

        // Separer les mots
        $table = preg_split("/ +/", $texte);

        while (list(, $mot) = each($table)) {
                if (strlen($mot) > $min_long) {
                        $h = substr(md5($mot), 0, 16);
                        $index[$h] += $val/(1+$translitteration_complexe);
                        $mots .= ",(0x$h,'$mot')";
                }
        }
}

function deja_indexe($type, $id_objet) {
        $table_index = 'spip_index_'.table_objet($type);
        $col_id = 'id_'.$type;
        $query = "SELECT $col_id FROM $table_index WHERE $col_id=$id_objet LIMIT 0,1";
        $n = @spip_num_rows(@spip_query($query));
        return ($n > 0);
}


// Extracteur des documents 'txt'
function extracteur_txt($fichier, &$charset) {
        lire_fichier($fichier, $contenu);

        // Reconnaitre le BOM utf-8 (0xEFBBBF)
        include_ecrire('inc_charsets.php3');
        if (bom_utf8($contenu))
                $charset = 'utf-8';

        return $contenu;
}

// Extracteur des documents 'html'
function extracteur_html($fichier, &$charset) {
        lire_fichier($fichier, $contenu);

        // Importer dans le charset local
        include_ecrire('inc_charsets.php3');
        $contenu = transcoder_page($contenu);
        $charset = lire_meta('charset');

        return $contenu;
}

// Quels formats sait-on extraire ?
$GLOBALS['extracteur'] = array (
        'txt'   => 'extracteur_txt',
        'pas'   => 'extracteur_txt',
        'c'     => 'extracteur_txt',
        'css'   => 'extracteur_txt',
        'html'  => 'extracteur_html'
);

// Indexer le contenu d'un document
function indexer_contenu_document ($row) {
        global $extracteur;

        if ($row['mode'] == 'vignette') return;
        list($extension) = spip_fetch_array(spip_query(
                "SELECT extension FROM spip_types_documents
                WHERE id_type = ".$row['id_type']
        ));

        // Voir si on sait lire le contenu (eventuellement en chargeant le
        // fichier extract_pdf.php dans find_in_path() )
        if ($plugin = find_in_path('extract_'.$extension.'.php')) {
                include_local($plugin);
        }
        if (function_exists($lire = $extracteur[$extension])) {
                // Voir si on a deja une copie du doc distant
                // Note: si copie_locale() charge le doc, elle demande une reindexation
                if (!$fichier = copie_locale($row['fichier'], 'test')) {
                        spip_log("pas de copie locale de '$fichier'");
                        return;
                }
                // par defaut, on pense que l'extracteur va retourner ce charset
                $charset = 'iso-8859-1'; 
                // lire le contenu
                $contenu = $lire($fichier, $charset);
                if (!$contenu) {
                        spip_log("Echec de l'extraction de '$fichier'");
                } else {
                        // Ne retenir que les 50 premiers ko
                        $contenu = substr($contenu, 0, 50000);
                        // importer le charset
                        $contenu = importer_charset($contenu, $charset);
                        // Indexer le texte
                        indexer_chaine($contenu, 1);
                }
        } else {
                spip_log("pas d'extracteur '$extension' fonctionnel");
        }
}


// Indexer les documents, auteurs, mots-cles associes a l'objet
function indexer_elements_associes($objet, $id_objet, $associe, $valeur) {
        switch ($associe) {
                case 'document':
                        $r = spip_query("SELECT doc.titre, doc.descriptif
                                FROM spip_documents AS doc,
                                spip_documents_".table_objet($objet)." AS lien
                                WHERE lien.".id_table_objet($objet)."=$id_objet
                                AND doc.id_document=lien.id_document");
                        while ($row = spip_fetch_array($r)) {
                                indexer_chaine($row['titre'],2 * $valeur);
                                indexer_chaine($row['descriptif'],1 * $valeur);
                        }
                        break;

                case 'auteur':
                        $r = spip_query("SELECT auteurs.nom
                                FROM spip_auteurs AS auteurs,
                                spip_auteurs_".table_objet($objet)." AS lien
                                WHERE lien.".id_table_objet($objet)."=$id_objet
                                AND auteurs.id_auteur=lien.id_auteur");
                        while ($row = spip_fetch_array($r)) {
                                indexer_chaine($row['nom'], 1 * $valeur, 2);
                        }
                        break;

                case 'mot':
                        $r = spip_query("SELECT mots.titre, mots.descriptif
                                FROM spip_mots AS mots,
                                spip_mots_".table_objet($objet)." AS lien
                                WHERE lien.".id_table_objet($objet)."=$id_objet
                                AND mots.id_mot = lien.id_mot");
                        while ($row = spip_fetch_array($r)) {
                                indexer_chaine($row['titre'],4 * $valeur);
                                indexer_chaine($row['descriptif'],1 * $valeur);
                        }
                        break;
        }
}


function indexer_objet($type, $id_objet, $forcer_reset = true) {
        global $index, $mots, $translitteration_complexe;

        $table = 'spip_'.table_objet($type);
        $table_index = 'spip_index_'.table_objet($type);
        $col_id = id_table_objet($type);

        if (!$id_objet) return;
        if (!$forcer_reset AND deja_indexe($type, $id_objet)) {
                spip_log ("$type $id_objet deja indexe");
                spip_query("UPDATE $table SET idx='oui' WHERE $col_id=$id_objet");
                return;
        }
        // marquer "en cours d'indexation"
        spip_query("UPDATE $table SET idx='idx' WHERE $col_id=$id_objet");

        include_ecrire("inc_texte.php3");
        include_ecrire("inc_filtres.php3");

        spip_log("indexation $type $id_objet");
        $index = '';
        $mots = '';

        $query = "SELECT * FROM $table WHERE $col_id=$id_objet";
        $result = spip_query($query);
        $row = spip_fetch_array($result);

        if (!$row) return;

        // translitteration complexe ?
        if (!$lang = $row['lang']) $lang = lire_meta('langue_site');
        if ($lang == 'de' OR $lang=='vi') {
                $translitteration_complexe = 1;
                spip_log ('-> translitteration complexe');
        } else $translitteration_complexe = 0;

        switch($type) {
        case 'article':
                indexer_chaine($row['titre'], 8);
                indexer_chaine($row['soustitre'], 5);
                indexer_chaine($row['surtitre'], 5);
                indexer_chaine($row['descriptif'], 4);
                indexer_chaine($row['chapo'], 3);
                indexer_chaine($row['texte'], 1);
                indexer_chaine($row['ps'], 1);
                indexer_chaine($row['nom_site'], 1);
                indexer_chaine(@join(' ', unserialize($row['extra'])), 1);
                indexer_elements_associes('article', $id_objet, 'document', 1);
                indexer_elements_associes('article', $id_objet, 'auteur', 10);
                indexer_elements_associes('article', $id_objet, 'mot', 3);
                break;

        case 'breve':
                indexer_chaine($row['titre'], 8);
                indexer_chaine($row['texte'], 2);
                indexer_chaine(@join(' ', unserialize($row['extra'])), 1);
                indexer_elements_associes('breve', $id_objet, 'document', 1);
                indexer_elements_associes('breve', $id_objet, 'mot', 3);
                break;

        case 'rubrique':
                indexer_chaine($row['titre'], 8);
                indexer_chaine($row['descriptif'], 5);
                indexer_chaine($row['texte'], 1);
                indexer_chaine(@join(' ', unserialize($row['extra'])), 1);
                indexer_elements_associes('rubrique', $id_objet, 'document', 1);
                indexer_elements_associes('rubrique', $id_objet, 'mot', 3);
                break;

        case 'auteur':
                indexer_chaine($row['nom'], 5, 2);
                indexer_chaine($row['bio'], 1);
                indexer_chaine(@join(' ', unserialize($row['extra'])), 1);
                break;

        case 'mot':
                indexer_chaine($row['titre'], 8);
                indexer_chaine($row['descriptif'], 5);
                indexer_chaine($row['texte'], 1);
                indexer_chaine(@join(' ', unserialize($row['extra'])), 1);
                break;

        case 'signature':
                indexer_chaine($row['nom_email'], 2, 2);
                indexer_chaine($row['ad_email'], 2);
                indexer_chaine($row['nom_site'], 2);
                indexer_chaine($row['url_site'], 1);
                indexer_chaine($row['message'], 1);
                break;

        case 'syndic':
                indexer_chaine($row['nom_site'], 50);
                indexer_chaine($row['descriptif'], 30);
                indexer_elements_associes('syndic', $id_objet, 'document', 1);
                indexer_elements_associes('syndic', $id_objet, 'mot', 3);

                // Ajouter les titres des articles syndiques de ce site, le cas echeant
                if ($row['syndication'] = "oui") {
                        $query_syndic = "SELECT titre FROM spip_syndic_articles
                        WHERE id_syndic=$id_objet AND statut='publie'
                        ORDER BY date DESC LIMIT 0,100";
                        $result_syndic = spip_query($query_syndic);
                        while ($row_syndic = spip_fetch_array($result_syndic)) {
                                indexer_chaine($row_syndic['titre'], 5);
                        }
                }
                // Aller chercher la page d'accueil
                if (lire_meta("visiter_sites") == "oui") {
                        include_ecrire ("inc_sites.php3");
                        spip_log ("indexation contenu syndic ".$row['url_site']);
                        indexer_chaine(supprimer_tags(
                                recuperer_page($row['url_site'], true, false, 50000)
                                ), 1);
                }
                break;

        //
        // Cas tres particulier du forum :
        // on indexe le thread comme un tout
        case 'forum':
                // 1. chercher la racine du thread
                $id_forum = $id_objet;
                while ($row['id_parent']) {
                        $id_forum = $row['id_parent'];
                        $s = spip_query("SELECT id_forum,id_parent FROM spip_forum WHERE id_forum=$id_forum");
                        $row = spip_fetch_array($s);
                }

                // 2. chercher tous les forums du thread
                // (attention le forum de depart $id_objet n'appartient pas forcement
                // a son propre thread car il peut etre le fils d'un forum non 'publie')
                $thread="$id_forum";
                $fini = false;
                while (!$fini) {
                        $s = spip_query("SELECT id_forum FROM spip_forum WHERE id_parent IN ($thread) AND id_forum NOT IN ($thread) AND statut='publie'");
                        if (spip_num_rows($s) == 0) $fini = true;
                        while ($t = spip_fetch_array($s))
                                $thread.=','.$t['id_forum'];
                }
                
                // 3. marquer le thread comme "en cours d'indexation"
                spip_log("-> indexation thread $thread");
                spip_query("UPDATE spip_forum SET idx='idx'
                        WHERE id_forum IN ($thread,$id_objet) AND idx!='non'");

                // 4. Indexer le thread
                $s = spip_query("SELECT * FROM spip_forum
                        WHERE id_forum IN ($thread) AND idx!='non'");
                while ($row = spip_fetch_array($s)) {
                        indexer_chaine($row['titre'], 3);
                        indexer_chaine($row['texte'], 1);
                        indexer_chaine($row['auteur'], 2, 2);
                        indexer_chaine($row['email_auteur'], 2);
                        indexer_chaine($row['nom_site'], 2);
                        indexer_chaine($row['url_site'], 1);
                }

                // 5. marquer le thread comme "indexe"
                spip_query("UPDATE spip_forum SET idx='oui'
                        WHERE id_forum IN ($thread,$id_objet) AND idx!='non'");

                // 6. Changer l'id_objet en id_forum de la racine du thread
                $id_objet = $id_forum;

                break;


        case 'document':
                // 1. Indexer le descriptif
                indexer_chaine($row['titre'], 20);
                indexer_chaine($row['descriptif'], 10);
                indexer_chaine(preg_replace(',^(IMG/|.*://),', '', $row['fichier']), 1);
                indexer_elements_associes('document', $id_objet, 'mot', 3);

                // 2. Indexer le contenu si on sait le lire
                indexer_contenu_document($row);
                break;


        } // switch

        $query = "DELETE FROM $table_index WHERE $col_id=$id_objet";
        $result = spip_query($query);

        if ($index) {
                if ($mots) {
                        $mots = "INSERT IGNORE INTO spip_index_dico (hash, dico) VALUES ".substr($mots,1);      // supprimer la virgule du debut
                        spip_query($mots);
                }
                reset($index);
                unset($q);
                while (list($hash, $points) = each($index)) $q[] = "(0x$hash,".ceil($points).",$id_objet)";
                spip_query("INSERT INTO $table_index (hash, points, $col_id) VALUES ".join(',',$q));
        }

        // marquer "indexe"
        spip_query("UPDATE $table SET idx='oui' WHERE $col_id=$id_objet");
}

/*
        Valeurs du champ 'idx' de la table spip_objet(s)
        '' ne sait pas
        '1' ˆ (re)indexer
        'oui' deja indexe
        'idx' en cours
        'non' ne jamais indexer
*/

// API pour l'espace prive
function marquer_indexer ($objet, $id_objet) {
        spip_log ("demande indexation $objet $id_objet");
        $table = 'spip_'.table_objet($objet);
        $id = id_table_objet($objet);
        spip_query ("UPDATE $table SET idx='1' WHERE $id=$id_objet AND idx!='non'");
}

// A garder pour compatibilite bouton memo...
function indexer_article($id_article) {
        marquer_indexer('article', $id_article);
}

// n'indexer que les objets publies
function critere_indexation($type) {
        switch ($type) {
                case 'article':
                case 'breve':
                case 'rubrique':
                case 'syndic':
                case 'forum':
                case 'signature':
                        $critere = "statut='publie'";
                        break;
                case 'auteur':
                        $critere = "statut IN ('0minirezo', '1comite')";
                        break;
                case 'mot':
                case 'document':
                default:
                        $critere = '1=1';
                        break;
        }
        return $critere;
}

function effectuer_une_indexation($nombre_indexations = 1) {

        // chercher un objet a indexer dans chacune des tables d'objets
        $vu = array();
        $types = array('article','auteur','breve','mot','rubrique','signature','syndic','forum','document');

        while (list(,$type) = each($types)) {
                $table_objet = 'spip_'.table_objet($type);
                $table_index = 'spip_index_'.table_objet($type);

                $critere = critere_indexation($type);

                if ($type == 'syndic' OR $type == 'document')
                        $limit = 1;
                else
                        $limit = $nombre_indexations;

                $s = spip_query("SELECT id_$type, idx FROM $table_objet WHERE idx IN ('','1','idx') AND $critere ORDER BY idx='idx',idx='' LIMIT 0,$limit");
                while ($t = spip_fetch_array($s)) {
                        $vu[$type] .= $t[0].", ";
                        indexer_objet($type, $t[0], $t[1]);
                }
        }
        return $vu;
}

function executer_une_indexation_syndic() {
        $id_syndic = 0;
        if ($row = spip_fetch_array(spip_query("SELECT id_syndic FROM spip_syndic WHERE statut='publie' AND date_index < DATE_SUB(NOW(), INTERVAL 7 DAY) ORDER BY date_index LIMIT 0,1"))) {
                $id_syndic = $row['id_syndic'];
                spip_query("UPDATE spip_syndic SET date_index=NOW() WHERE id_syndic=$id_syndic");
                marquer_indexer('syndic', $id_syndic);
        }
        return $id_syndic;
}

function creer_liste_indexation() {
        $types = array('article','auteur','breve','mot','rubrique','syndic','forum','signature','document');
        while (list(,$type) = each($types)) {
                $table = 'spip_'.table_objet($type);
                spip_query("UPDATE $table SET idx='1' WHERE idx!='non'");
        }
}

function purger_index() {
                spip_query("DELETE FROM spip_index_articles");
                spip_query("DELETE FROM spip_index_auteurs");
                spip_query("DELETE FROM spip_index_breves");
                spip_query("DELETE FROM spip_index_mots");
                spip_query("DELETE FROM spip_index_rubriques");
                spip_query("DELETE FROM spip_index_syndic");
                spip_query("DELETE FROM spip_index_forum");
                spip_query("DELETE FROM spip_index_signatures");
                spip_query("DELETE FROM spip_index_documents");
                spip_query("DELETE FROM spip_index_dico");
}

// cree la requete pour une recherche en txt integral
function requete_txt_integral($objet, $hash_recherche) {
        $table = "spip_".table_objet($objet);
        $index_table = "spip_index_".table_objet($objet);
        $id_objet = "id_".$objet;
        return "SELECT objet.*, SUM(rec.points) AS points
                FROM $table AS objet, $index_table AS rec
                WHERE objet.$id_objet = rec.$id_objet
                AND rec.hash IN ($hash_recherche)
                GROUP BY objet.$id_objet
                ORDER BY points DESC
                LIMIT 0,10";
}

// rechercher un mot dans le dico
// retourne deux methodes : lache puis strict
function requete_dico($val) {
        $min_long = 3;

        // cas normal
        if (strlen($val) > $min_long) {
                return array("dico LIKE '".addslashes($val)."%'", "dico = '".addslashes($val)."'");
        } else
                return array("dico = '".addslashes($val)."___'", "dico = '".addslashes($val)."___'");
}


// decode la chaine recherchee et la traduit en hash
function requete_hash ($rech) {
        // recupere les mots de la recherche
        $translitteration_complexe = true;
        $rech = nettoyer_chaine_indexation($rech);
        $regs = separateurs_indexation(true)." ";
        $rech = strtr($rech, $regs, ereg_replace('.', ' ', $regs));
        $s = preg_split("/ +/", $rech);
        unset($dico);
        unset($h);

        // cherche les mots dans le dico
        while (list(, $val) = each($s)) {
                list($rq, $rq_strict) = requete_dico ($val);
                if ($rq)
                        $dico[] = $rq;
                if ($rq_strict)
                        $dico_strict[] = $rq_strict;
        }

        // Attention en MySQL 3.x il faut passer par HEX(hash)
        // alors qu'en MySQL 4.1 c'est interdit !
        $vers = spip_fetch_array(spip_query("SELECT VERSION()"));
        if (substr($vers[0], 0, 1) >= 4
        AND substr($vers[0], 2, 1) >= 1 ) {
                $hex_fmt = '';
                $select_hash = 'hash AS h';
        } else {
                $hex_fmt = '0x';
                $select_hash = 'HEX(hash) AS h';
        }

        // compose la recherche dans l'index
        if ($dico_strict) {
                $query2 = "SELECT $select_hash FROM spip_index_dico WHERE "
                        .join(" OR ", $dico_strict);
                $result2 = spip_query($query2);
                while ($row2 = spip_fetch_array($result2))
                        $h_strict[] = $hex_fmt.$row2['h'];
        }
        if ($dico) {
                $query2 = "SELECT $select_hash FROM spip_index_dico WHERE "
                        .join(" OR ", $dico);
                $result2 = spip_query($query2);
                while ($row2 = spip_fetch_array($result2))
                        $h[] = $hex_fmt.$row2['h'];
        }
        if ($h_strict)
                $hash_recherche_strict = join(",", $h_strict);
        else
                $hash_recherche_strict = "0";

        if ($h)
                $hash_recherche = join(",", $h);
        else
                $hash_recherche = "0";

        return array($hash_recherche, $hash_recherche_strict);
}


function prepare_recherche($recherche, $type = 'id_article', $table='articles') {
        static $cache = array();

        if (!$cache[$type][$recherche]) {

                if (!$cache['hash'][$recherche])
                        $cache['hash'][$recherche] = requete_hash($recherche);
                list($hash_recherche, $hash_recherche_strict)
                        = $cache['hash'][$recherche];

                $strict = array();
                if ($hash_recherche_strict)
                        foreach (split(',',$hash_recherche_strict) as $h)
                                $strict[$h] = 99;

                $points = array();
                $s = spip_query ("SELECT hash,points,$type as id
                        FROM spip_index_$table
                        WHERE hash IN ($hash_recherche)");

                while ($r = spip_fetch_array($s))
                        $points[$r['id']]
                        += (1 + $strict[$r['hash']]) * $r['points'];
                spip_free_result($s);

                arsort($points, SORT_NUMERIC);

                # calculer le {id_article IN()} et le {... as points}
                if (!count($points)) {
                        $cache[$type][$recherche] = array('', '');
                } else {
                        $ids = array();
                        $select = '0';
                        foreach ($points as $id => $p)
                                $listes_ids[$p] .= ','.$id;
                        foreach ($listes_ids as $p => $liste_ids)
                                $select .= "+$p*(".calcul_mysql_in("$table.$type", substr($liste_ids, 1)).") ";

                        $cache[$type][$recherche] = array($select, 
                                                          '('.calcul_mysql_in("$table.$type", join(',',array_keys($points))).')');
                }
        }

        return $cache[$type][$recherche];
}

?>