[SPIP] +2.1.12
[velocampus/web/www.git] / www / plugins / auto / couteau_suisse / couteau_suisse / outils / glossaire_fonctions.php
1 <?php
2
3 // Outils GLOSSAIRE - 26 mai 2007
4 // Serieuse refonte et integration au Couteau Suisse : Patrice Vanneufville
5 // Doc : http://www.spip-contrib.net/?article2206
6
7 include_spip('inc/charsets');
8
9 // liste des accents (sans casse)
10 define('_GLOSSAIRE_ACCENTS', '#(19[2-9]|2[023][0-9]|21[0-46-9]|24[0-689]|25[0-4]|33[89]|35[23]|376)|a(?:acute|circ|elig|grave|ring|tilde|uml)|ccedil|e(?:acute|circ|grave|th|uml)|i(?:acute|circ|grave|uml)|ntilde|o(?:acute|circ|elig|grave|slash|tilde|uml)|s(?:caron|zlig)|thorn|u(?:acute|circ|grave|uml)|y(?:acute|uml)');
11
12 // on calcule ici la constante _GLOSSAIRE_QUERY, surchargeable dans config/mes_options.php
13 function glossaire_groupes() {
14 $groupes = trim($GLOBALS['glossaire_groupes']);
15 if(!strlen($groupes)) return _q('Glossaire');
16 else {
17 $groupes = explode(':', $groupes);
18 foreach($groupes as $i=>$g) $groupes[$i] = _q(trim($g));
19 return join(" OR type=", $groupes);
20 }
21 }
22
23 // Separateur des titres de mots stockes en base
24 @define('_GLOSSAIRE_TITRE_BASE_SEP', '/');
25 // Separateur utilise pour fabriquer le titre de la fenetre de glossaire (fichiers fonds/glossaire_xx.html).
26 @define('_GLOSSAIRE_TITRE_SEP', '<br />');
27 // chaine pour interroger la base (SPIP <= 1.92)
28 if(!defined('_SPIP19300'))
29 @define('_GLOSSAIRE_QUERY', 'SELECT id_mot, titre, texte, descriptif FROM spip_mots WHERE type=' . glossaire_groupes() . ' ORDER BY id_mot ASC');
30
31 // surcharge possible de cette fonction glossaire_generer_url_dist par : glossaire_generer_url($id_mot, $titre_mot)
32 // si elle existe, elle sera utilisee pour generer l'url cliquable des mots trouves
33 // exemple pour annuler le clic : function glossaire_generer_url($id_mot, $titre_mot) { return 'javascript:;'; }
34 function glossaire_generer_url_dist($id_mot, $titre_mot) {
35 if(defined('_SPIP19300'))
36 return generer_url_entite($id_mot, 'mot'); // depuis SPIP 2.0
37 else { charger_generer_url(); return generer_url_mot($id_mot); } // avant SPIP 2.0
38 }
39
40 // surcharge possible de cette fonction glossaire_generer_mot_dist par : glossaire_generer_mot($id_mot, $mot)
41 // si elle existe, elle sera utilisee pour remplacer le mot detecte dans la phrase
42 /* exemple pour utiliser un fond personnalise, mettre une couleur de groupe ou inserer un logo par exemple :
43 function glossaire_generer_mot($id_mot, $mot) {
44 return recuperer_fond('/fonds/mon_glossaire', array('id_mot'=>$id_mot, 'mot'=>$mot));
45 }*/
46 function glossaire_generer_mot_dist($id_mot, $mot) {
47 return $mot;
48 }
49
50 // traitement pour #TITRE/mots : retrait des expressions regulieres
51 function cs_glossaire_titres($titre) {
52 if(strpos($titre, ',')===false) return $titre;
53 list(,,$mots) = glossaire_parse($titre);
54 return $mots;
55 }
56
57 // Cette fonction retire du texte les boites de definition et les liens du glossaire
58 function cs_retire_glossaire($texte) {
59 $texte = preg_replace(',<span class="gl_(jst?|d[td])".*?</span>,s', '', $texte);
60 if(!defined('_GLOSSAIRE_JS')) $texte = preg_replace(',<span class="gl_dl">.*?</span>,s', '', $texte);
61 return preg_replace(',<a [^>]+class=\'cs_glossaire\'><span class=\'gl_mot\'>(.*?)</span></a>,s', '$1', $texte);
62 }
63 $GLOBALS['cs_introduire'][] = 'cs_retire_glossaire';
64
65 // remplace les accents unicode par l'equivalent charset/unicode/html
66 function glossaire_accents($regexpr) {
67 if (strpos($regexpr, '&')===false) return $regexpr;
68 return preg_replace_callback(",&#([0-9]+);,", 'glossaire_accents_callback', str_replace('& ','&amp; ',$regexpr));
69 }
70
71 // $matches est un caractere unicode sous forme &#XXX;
72 // ici on cherche toutes les formes de ce caractere, minuscule ou majuscule : unicode, charset et html
73 function glossaire_accents_callback($matches) {
74 $u = unicode2charset($matches[0]); // charset
75 $u2 = init_mb_string()?mb_strtoupper($u):strtoupper($u); // charset majuscule
76 $u3 = htmlentities($u2, ENT_QUOTES, $GLOBALS['meta']['charset']); // html majuscule
77 $u4 = html2unicode($u3); // unicode majuscule
78 $a = array_unique(array($u, $u2, htmlentities($u, ENT_QUOTES, $GLOBALS['meta']['charset']), $u3, $matches[0], $u4));
79 // $a = array_unique(array($u, htmlentities($u, ENT_QUOTES, $GLOBALS['meta']['charset']), $matches[0]));
80 return '(?:'.join('|', $a).')';
81 }
82 function glossaire_echappe_balises_callback($matches) {
83 global $gloss_ech, $gloss_ech_id;
84 $gloss_ech[] = $matches[0];
85 return '@@E'.$gloss_ech_id++.'@@';
86 }
87 function glossaire_echappe_mot_callback($matches) {
88 global $gloss_mots, $gloss_mots_id, $gloss_id;
89 $gloss_mots[] = $matches[0];
90 return '@@M'.$gloss_mots_id++.'#'.$gloss_id.'@@';
91 }
92
93 function glossaire_safe($texte) {
94 // on retire les notes avant propre()
95 return safehtml(cs_propre(preg_replace(', *\[\[(.*?)\]\],msS', '', nl2br(trim($texte)))));
96 }
97
98 // renvoie le tableau des mots du glossaire
99 function glossaire_query_tab() {
100 // interrogation personnalisee de la base
101 if(defined('_GLOSSAIRE_QUERY')) {
102 $res = array();
103 $fetch = function_exists('sql_fetch')?'sql_fetch':'spip_fetch_array';
104 $query = spip_query(_GLOSSAIRE_QUERY);
105 while($r = $fetch($query)) $res[] = $r;
106 return $res;
107 }
108 return sql_allfetsel('id_mot,titre,texte,descriptif', 'spip_mots', 'type='.glossaire_groupes(), '', 'id_mot ASC');
109 }
110
111 // parse toutes les formes du titre d'un mot-cle du glossaire
112 // prendre en compte les formes du mot : architrave/architraves
113 function glossaire_parse($titre) {
114 $mots = $regs = $titres = array();
115 foreach(explode(_GLOSSAIRE_TITRE_BASE_SEP, str_replace('</','@@tag@@',$titre)) as $m) {
116 // interpretation des expressions regulieres grace aux virgules : ,un +mot,i
117 $m = trim(str_replace('@@tag@@','</',$m));
118 if(strncmp($m,',',1)===0) $regs[] = $m;
119 else {
120 $mots[] = charset2unicode($m);
121 $titres[] = $m;
122 }
123 }
124 if(count($titres))
125 $titres = join(_GLOSSAIRE_TITRE_SEP, $titres);
126 elseif(count($regs)) {
127 preg_match('/^,(.*),\w*$/', $regs[0], $rr);
128 if (strpos($titres = $rr[1], '\\')!==false) {
129 $titres = preg_replace('@\\\\([\.\\\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|:,-])@', '$1', $titres);
130 $titres = preg_replace(',\\\\[bswd],i', '', $titres);
131 }
132 } else
133 $titres = '??';
134 if(count($mots)) {
135 $mots = array_unique($mots);
136 array_walk($mots, 'cs_preg_quote');
137 $mots = glossaire_accents(join('|', $mots));
138 } else $mots = '';
139 return array($mots, $regs, $titres);
140 }
141
142 function glossaire_gogogo($texte, $mots, $limit, &$unicode) {
143 // prudence 2 : on protege TOUTES les balises HTML comprenant le mot
144 if (strpos($texte, '<')!==false)
145 $texte = preg_replace_callback(",<[^>]*(?:$mots)[^>]*>,Ui", 'glossaire_echappe_balises_callback', $texte);
146 // prudence 3 : en iso-8859-1, (\W) comprend les accents, mais pas en utf-8... Donc on passe en unicode
147 if(($GLOBALS['meta']['charset'] != 'iso-8859-1') && !$unicode)
148 { $texte = charset2unicode($texte); $unicode = true; }
149 // prudence 4 : on neutralise le mot si on trouve un accent (HTML ou unicode) juste avant ou apres
150 if (strpos($texte, '&')!==false) {
151 $texte = preg_replace_callback(',&(?:'._GLOSSAIRE_ACCENTS.");(?:$mots),i", 'glossaire_echappe_balises_callback', $texte);
152 $texte = preg_replace_callback(",(?:$mots)&(?:"._GLOSSAIRE_ACCENTS.');,i', 'glossaire_echappe_balises_callback', $texte);
153 }
154 // a chaque mot reconnu, on pose une balise temporaire cryptee
155 return trim(preg_replace_callback(",(?<=\W)(?:$mots)(?=\W),i", "glossaire_echappe_mot_callback", " $texte ", $limit));
156 }
157
158 // cette fonction n'est pas appelee dans les balises html : html|code|cadre|frame|script|acronym|cite|a
159 // si $liste=true alors la fonction renvoie la liste des mots trouves
160 // chaque element du tableau renvoye est array('mot trouve', id_mot, 'lien mot', 'titre mot');
161 function cs_rempl_glossaire($texte, $liste=false) {
162 global $gloss_id, $gloss_mots, $gloss_mots_id, $gloss_ech, $gloss_ech_id;
163 // si [!glossaire] est trouve on sort
164 if(strpos($texte, _CS_SANS_GLOSSAIRE)!==false)
165 return $liste?array():str_replace(_CS_SANS_GLOSSAIRE, '', $texte);
166 // mise en static de la table des mots pour eviter d'interrroger la base a chaque fois
167 // attention aux besoins de memoire...
168 static $limit, $glossaire_generer_url, $glossaire_generer_mot, $glossaire_array = NULL;
169 if(!isset($glossaire_array)) {
170 $glossaire_array = glossaire_query_tab();
171 $glossaire_generer_url = function_exists('glossaire_generer_url')?'glossaire_generer_url':'glossaire_generer_url_dist';
172 $limit = defined('_GLOSSAIRE_LIMITE')?_GLOSSAIRE_LIMITE:-1;
173 $glossaire_generer_mot = function_exists('glossaire_generer_mot')
174 ?'glossaire_generer_mot(\'\\2\', $GLOBALS[\'gloss_mots\'][\\1])':'$GLOBALS[\'gloss_mots\'][\\1]'; // 'glossaire_generer_mot_dist(\'\\2\', $GLOBALS[\'gloss_mots\'][\\1])';
175 $glossaire_generer_mot = '"<a $table1[\\2]_".$GLOBALS["gl_i"]++."\' class=\'cs_glossaire\'><span class=\'gl_mot\'>".'.$glossaire_generer_mot.'."</span>$table2[\\2]</a>"';
176 }
177 $unicode = false;
178 // initialisation des globales d'echappement
179 $gloss_ech = $gloss_mots = array();
180 $gloss_ech_id = $gloss_mots_id = 0;
181 // prudence 1 : protection des liens SPIP
182 if (strpos($texte, '[')!==false)
183 $texte = preg_replace_callback(',\[[^][]*->>?[^]]*\],msS', 'glossaire_echappe_balises_callback', $texte);
184 // parcours de tous les mots, sauf celui qui peut faire partie du contexte (par ex : /spip.php?mot5)
185 $mot_contexte=$GLOBALS['contexte']['id_mot']?$GLOBALS['contexte']['id_mot']:_request('id_mot');
186 foreach ($glossaire_array as $mot) if (($gloss_id = $mot['id_mot']) <> $mot_contexte) {
187 // parser le mot-cle du glossaire
188 // contexte de langue a prendre en compte ici
189 list($les_mots, $les_regexp, $les_titres) = glossaire_parse($titre=extraire_multi($mot['titre']));
190 $mot_present = false;
191 if(count($les_regexp)) {
192 // a chaque expression reconnue, on pose une balise temporaire cryptee
193 // ce remplacement est puissant, attention aux balises HTML ; par exemple, eviter : ,div,i
194 $texte = preg_replace_callback($les_regexp, "glossaire_echappe_mot_callback", $texte, $limit);
195 // TODO 1 : sous PHP 5.0, un parametre &$count permet de savoir si un remplacement a eu lieu
196 // et s'il faut construire la fenetre de glossaire.
197 // TODO 2 : decrementer le parametre $limit pour $les_mots, si &$count est renseigne.
198 // en attendant, constuisons qd meme la fenetre...
199 $mot_present = true;
200 }
201 if($les_mots) {
202 if(preg_match(",\W(?:$les_mots)\W,i", " $texte ")) {
203 $texte = glossaire_gogogo($texte, $les_mots, $limit, $unicode);
204 $mot_present = true;
205 }
206 }
207 // si un mot est trouve, on construit la fenetre de glossaire
208 if($mot_present) {
209 $lien = $glossaire_generer_url($gloss_id, $titre);
210 // $definition =strlen($mot['descriptif'])?$mot['descriptif']:$mot['texte'];
211 if($liste)
212 $table1[$gloss_id] = array($gloss_id, $lien, $les_titres);
213 else {
214 $table1[$gloss_id] = "href='$lien' name='mot$gloss_id"; // name est complete plus tard pour eviter les doublons
215 $table2[$gloss_id] = defined('_CS_PRINT')?'':recuperer_fond(
216 defined('_GLOSSAIRE_JS')?'fonds/glossaire_js':'fonds/glossaire_css',
217 array('id_mot' => $gloss_id, 'titre' => $les_titres,
218 'texte' => glossaire_safe($mot['texte']),
219 'descriptif' => glossaire_safe($mot['descriptif'])));
220 }
221 }
222 }
223 $GLOBALS['gl_i'] = 0;
224 if($liste) $texte = (preg_match_all(',@@M(\d+)#(\d+)@@,', $texte, $reg, PREG_SET_ORDER)
225 && array_walk($reg,
226 create_function('&$v,$k,&$t1', '$v=array_merge(array($GLOBALS[\'gloss_mots\'][$v[1]]),$t1[$v[2]]);'), $table1)
227 )?$reg:array();
228 else {
229 // remplacement des echappements
230 $texte = preg_replace(',@@E(\d+)@@,e', '$GLOBALS[\'gloss_ech\'][\\1]', $texte);
231 // remplacement final des balises posees ci-dessus
232 $texte = preg_replace(',@@M(\d+)#(\d+)@@,e', $glossaire_generer_mot, $texte);
233 }
234 // nettoyage
235 unset($gloss_id, $gloss_mots, $gloss_mots_id, $gloss_ech, $gloss_ech_id);
236 return $texte;
237 }
238
239 // filtre appliquant l'insertion du glossaire
240 function cs_glossaire($texte) {
241 return cs_echappe_balises('html|code|cadre|frame|script|acronym|cite|a', 'cs_rempl_glossaire', $texte);
242 }
243
244 // filtre renvoyant la liste des mots trouves dans le texte
245 function cs_mots_glossaire($texte, $type='', $sep='') {
246 if(strpos($texte, "<span class='gl_mot'>")!==false && preg_match_all(",'gl_mot'>(.*?)</span>,", $texte, $reg))
247 // glossaire deja present, on simplifie donc le texte
248 $texte = join(' ', $reg[1]);
249 $mots = cs_echappe_balises('html|code|cadre|frame|script|acronym|cite|a', 'cs_rempl_glossaire', $texte, true);
250 if(!count($mots)) return strlen($sep)?'':$mots;
251 $lien = '$v="<a href=\"$v[2]\"';
252 $titre = strpos($type,'_unique')===false?'str_replace("<br />"," / ", $v[3])':'array_shift(explode(_GLOSSAIRE_TITRE_SEP, $v[3]))';
253 switch($type) {
254 case '':return $mots;
255 case 'id_mot':
256 array_walk($mots, create_function('&$v', '$v=$v[1];'));
257 break;
258 case 'mot':
259 array_walk($mots, create_function('&$v', '$v=$v[0];'));
260 break;
261 case 'titre': case 'titre_unique':
262 array_walk($mots, create_function('&$v', "\$v=$titre;"));
263 break;
264 case 'lien_mot':
265 array_walk($mots, create_function('&$v', $lien.'>$v[0]</a>";'));
266 break;
267 case 'lien_titre': case 'lien_titre_unique':
268 array_walk($mots, create_function('&$v', $lien.'>".'.$titre.'."</a>";'));
269 break;
270 case 'nuage': case 'nuage_unique':
271 $stats = array(); $min = 999999; $max = 0;
272 foreach($mots as $m) $stats[$m[1]]++;
273 $m = min($stats); $d = max($stats) - $m;
274 array_walk($stats, create_function('&$v', $d?"\$v=round((\$v-$m)*9/$d)+1;":'$v=1;')); // valeurs de 1 a 10
275 array_walk($mots, create_function('&$v,$k,&$s', $lien.' class=\"nuage".$s[$v[1]]."\">".'.$titre.'."</a>";'), $stats);
276 break;
277 default:return "#GLOSSAIRE/$type?";
278 }
279 $mots = array_unique($mots);
280 return strlen($sep)?join($sep, $mots):$mots;
281 }
282
283 ?>