[SPIP] v3.2.7-->v3.2.9
[lhc/web/www.git] / www / plugins-dist / textwheel / inc / texte.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2020 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 /**
14 * Gestion des textes et raccourcis SPIP
15 *
16 * Surcharge de ecrire/inc/texte
17 *
18 * @package SPIP\Textwheel\Texte
19 **/
20
21 if (!defined('_ECRIRE_INC_VERSION')) {
22 return;
23 }
24
25 include_spip('inc/texte_mini');
26 include_spip('inc/lien');
27
28 include_spip('inc/textwheel');
29
30
31 defined('_AUTOBR') || define('_AUTOBR', "<br class='autobr' />");
32 define('_AUTOBR_IGNORER', _AUTOBR ? "<!-- ig br -->" : "");
33
34 // Avec cette surcharge, cette globale n'est plus définie, et du coup ça plante dans les plugins qui font un foreach dessus comme ZPIP
35 $GLOBALS['spip_raccourcis_typo'] = array();
36 if (!isset($GLOBALS['toujours_paragrapher'])) {
37 $GLOBALS['toujours_paragrapher'] = true;
38 }
39
40 // class_spip : savoir si on veut class="spip" sur p i strong & li
41 // class_spip_plus : class="spip" sur les ul ol h3 hr quote table...
42 // la difference c'est que des css specifiques existent pour les seconds
43 //
44 if (!isset($GLOBALS['class_spip'])) {
45 $GLOBALS['class_spip'] = '';
46 }
47 if (!isset($GLOBALS['class_spip_plus'])) {
48 $GLOBALS['class_spip_plus'] = ' class="spip"';
49 }
50
51
52 /**
53 * Échapper et affichier joliement les `<script` ...
54 *
55 * @param string $t
56 * @return string
57 */
58 function echappe_js($t) {
59 static $wheel = null;
60
61 if (!isset($wheel)) {
62 $wheel = new TextWheel(
63 SPIPTextWheelRuleset::loader($GLOBALS['spip_wheels']['echappe_js'])
64 );
65 }
66
67 try {
68 $t = $wheel->text($t);
69 } catch (Exception $e) {
70 erreur_squelette($e->getMessage());
71 // sanitizer le contenu methode brute, puisqu'on a pas fait mieux
72 $t = textebrut($t);
73 }
74
75 return $t;
76 }
77
78
79 /**
80 * Paragrapher seulement
81 *
82 * Fermer les paragraphes ; Essaie de préserver des paragraphes indiqués
83 * à la main dans le texte (par ex: on ne modifie pas un `<p align='center'>`)
84 *
85 * @param string $t
86 * Le texte
87 * @param null $toujours_paragrapher
88 * true pour forcer les `<p>` même pour un seul paragraphe
89 * @return string
90 * Texte paragraphé
91 */
92 function paragrapher($t, $toujours_paragrapher = null) {
93 static $wheel = array();
94 if (is_null($toujours_paragrapher)) {
95 $toujours_paragrapher = $GLOBALS['toujours_paragrapher'];
96 }
97
98 if (!isset($wheel[$toujours_paragrapher])) {
99 $ruleset = SPIPTextWheelRuleset::loader($GLOBALS['spip_wheels']['paragrapher']);
100 if (!$toujours_paragrapher
101 and $rule = $ruleset->getRule('toujours-paragrapher')
102 ) {
103 $rule->disabled = true;
104 $ruleset->addRules(array('toujours-paragrapher' => $rule));
105 }
106 $wheel[$toujours_paragrapher] = new TextWheel($ruleset);
107 }
108
109 try {
110 $t = $wheel[$toujours_paragrapher]->text($t);
111 } catch (Exception $e) {
112 erreur_squelette($e->getMessage());
113 }
114
115 return $t;
116 }
117
118 /**
119 * Empêcher l'exécution de code PHP et JS
120 *
121 * Sécurité : empêcher l'exécution de code PHP, en le transformant en joli code
122 * dans l'espace privé. Cette fonction est aussi appelée par propre et typo.
123 *
124 * De la même manière, la fonction empêche l'exécution de JS mais selon le mode
125 * de protection passe en argument
126 *
127 * Il ne faut pas désactiver globalement la fonction dans l'espace privé car elle protège
128 * aussi les balises des squelettes qui ne passent pas forcement par propre ou typo après
129 * si elles sont appelées en direct
130 *
131 * @param string $arg
132 * Code à protéger
133 * @param int $mode_filtre
134 * Mode de protection
135 * -1 : protection dans l'espace privé et public
136 * 0 : protection dans l'espace public
137 * 1 : aucune protection
138 * utilise la valeur de la globale filtrer_javascript si non fourni
139 * @return string
140 * Code protégé
141 **/
142 function interdire_scripts($arg, $mode_filtre=null) {
143 // on memorise le resultat sur les arguments non triviaux
144 static $dejavu = array();
145 static $wheel = array();
146
147 if (is_null($mode_filtre) or !in_array($mode_filtre, array(-1, 0, 1))) {
148 $mode_filtre = $GLOBALS['filtrer_javascript'];
149 }
150
151 // Attention, si ce n'est pas une chaine, laisser intact
152 if (!$arg or !is_string($arg) or !strstr($arg, '<')) {
153 return $arg;
154 }
155 if (isset($dejavu[$mode_filtre][$arg])) {
156 return $dejavu[$mode_filtre][$arg];
157 }
158
159 if (!isset($wheel[$mode_filtre])) {
160 $ruleset = SPIPTextWheelRuleset::loader(
161 $GLOBALS['spip_wheels']['interdire_scripts']
162 );
163 // Pour le js, trois modes : parano (-1), prive (0), ok (1)
164 // desactiver la regle echappe-js si besoin
165 if ($mode_filtre == 1
166 or ($mode_filtre == 0 and !test_espace_prive())
167 ) {
168 $ruleset->addRules(array('securite-js' => array('disabled' => true)));
169 }
170 $wheel[$mode_filtre] = new TextWheel($ruleset);
171 }
172
173 try {
174 $t = $wheel[$mode_filtre]->text($arg);
175 } catch (Exception $e) {
176 erreur_squelette($e->getMessage());
177 // sanitizer le contenu methode brute, puisqu'on a pas fait mieux
178 $t = textebrut($arg);
179 }
180
181 // Reinserer les echappements des modeles
182 if (defined('_PROTEGE_JS_MODELES')) {
183 $t = echappe_retour($t, "javascript" . _PROTEGE_JS_MODELES);
184 }
185 if (defined('_PROTEGE_PHP_MODELES')) {
186 $t = echappe_retour($t, "php" . _PROTEGE_PHP_MODELES);
187 }
188
189 return $dejavu[$mode_filtre][$arg] = $t;
190 }
191
192
193 /**
194 * Applique la typographie générale
195 *
196 * Effectue un traitement pour que les textes affichés suivent les règles
197 * de typographie. Fait une protection préalable des balises HTML et SPIP.
198 * Transforme les balises `<multi>`
199 *
200 * @filtre
201 * @uses traiter_modeles()
202 * @uses corriger_typo()
203 * @uses echapper_faux_tags()
204 * @see propre()
205 *
206 * @param string $letexte
207 * Texte d'origine
208 * @param bool $echapper
209 * Échapper ?
210 * @param string|null $connect
211 * Nom du connecteur à la bdd
212 * @param array $env
213 * Environnement (pour les calculs de modèles)
214 * @return string $t
215 * Texte transformé
216 **/
217 function typo($letexte, $echapper = true, $connect = null, $env = array()) {
218 // Plus vite !
219 if (!$letexte) {
220 return $letexte;
221 }
222
223 // les appels directs a cette fonction depuis le php de l'espace
224 // prive etant historiquement ecrit sans argment $connect
225 // on utilise la presence de celui-ci pour distinguer les cas
226 // ou il faut passer interdire_script explicitement
227 // les appels dans les squelettes (de l'espace prive) fournissant un $connect
228 // ne seront pas perturbes
229 $interdire_script = false;
230 if (is_null($connect)) {
231 $connect = '';
232 $interdire_script = true;
233 $env['espace_prive'] = test_espace_prive();
234 }
235
236 $echapper = ($echapper ? 'TYPO' : false);
237 // Echapper les codes <html> etc
238 if ($echapper) {
239 $letexte = echappe_html($letexte, $echapper);
240 }
241
242 //
243 // Installer les modeles, notamment images et documents ;
244 //
245 // NOTE : propre() ne passe pas par ici mais directement par corriger_typo
246 // cf. inc/lien
247
248 $letexte = traiter_modeles($mem = $letexte, false, $echapper ? $echapper : '', $connect, null, $env);
249 if (!$echapper and $letexte != $mem) {
250 $echapper = '';
251 }
252 unset($mem);
253
254 $letexte = corriger_typo($letexte);
255 $letexte = echapper_faux_tags($letexte);
256
257 // reintegrer les echappements
258 if ($echapper !== false) {
259 $letexte = echappe_retour($letexte, $echapper);
260 }
261
262 // Dans les appels directs hors squelette, securiser ici aussi
263 if ($interdire_script) {
264 $letexte = interdire_scripts($letexte);
265 }
266
267 // Dans l'espace prive on se mefie de tout contenu dangereux
268 // https://core.spip.net/issues/3371
269 // et aussi dans l'espace public si la globale filtrer_javascript = -1
270 // https://core.spip.net/issues/4166
271 if ($GLOBALS['filtrer_javascript'] == -1
272 or (isset($env['espace_prive']) and $env['espace_prive'] and $GLOBALS['filtrer_javascript']<=0)) {
273 $letexte = echapper_html_suspect($letexte);
274 }
275
276 return $letexte;
277 }
278
279 // Correcteur typographique
280
281 define('_TYPO_PROTEGER', "!':;?~%-");
282 define('_TYPO_PROTECTEUR', "\x1\x2\x3\x4\x5\x6\x7\x8");
283
284 define('_TYPO_BALISE', ",</?[a-z!][^<>]*[" . preg_quote(_TYPO_PROTEGER) . "][^<>]*>,imsS");
285
286 /**
287 * Corrige la typographie
288 *
289 * Applique les corrections typographiques adaptées à la langue indiquée.
290 *
291 * @pipeline_appel pre_typo
292 * @pipeline_appel post_typo
293 * @uses corriger_caracteres()
294 * @uses corriger_caracteres()
295 *
296 * @param string $t Texte
297 * @param string $lang Langue
298 * @return string Texte
299 */
300 function corriger_typo($t, $lang = '') {
301 static $typographie = array();
302 // Plus vite !
303 if (!$t) {
304 return $t;
305 }
306
307 $t = pipeline('pre_typo', $t);
308
309 // Caracteres de controle "illegaux"
310 $t = corriger_caracteres($t);
311
312 // Proteger les caracteres typographiques a l'interieur des tags html
313 if (preg_match_all(_TYPO_BALISE, $t, $regs, PREG_SET_ORDER)) {
314 foreach ($regs as $reg) {
315 $insert = $reg[0];
316 // hack: on transforme les caracteres a proteger en les remplacant
317 // par des caracteres "illegaux". (cf corriger_caracteres())
318 $insert = strtr($insert, _TYPO_PROTEGER, _TYPO_PROTECTEUR);
319 $t = str_replace($reg[0], $insert, $t);
320 }
321 }
322
323 // trouver les blocs idiomes et les traiter à part
324 $t = extraire_idiome($ei = $t, $lang, true);
325 $ei = ($ei !== $t);
326
327 // trouver les blocs multi et les traiter à part
328 $t = extraire_multi($em = $t, $lang, true);
329 $em = ($em !== $t);
330
331 // Charger & appliquer les fonctions de typographie
332 $idxl = "$lang:" . (isset($GLOBALS['lang_objet']) ? $GLOBALS['lang_objet'] : $GLOBALS['spip_lang']);
333 if (!isset($typographie[$idxl])) {
334 $typographie[$idxl] = charger_fonction(lang_typo($lang), 'typographie');
335 }
336 $t = $typographie[$idxl]($t);
337
338 // Les citations en une autre langue, s'il y a lieu
339 if ($ei) {
340 $t = echappe_retour($t, 'idiome');
341 }
342 if ($em) {
343 $t = echappe_retour($t, 'multi');
344 }
345
346 // Retablir les caracteres proteges
347 $t = strtr($t, _TYPO_PROTECTEUR, _TYPO_PROTEGER);
348
349 // pipeline
350 $t = pipeline('post_typo', $t);
351
352 # un message pour abs_url - on est passe en mode texte
353 $GLOBALS['mode_abs_url'] = 'texte';
354
355 return $t;
356 }
357
358
359 //
360 // Tableaux
361 //
362
363 define('_RACCOURCI_TH_SPAN', '\s*(:?{{[^{}]+}}\s*)?|<');
364
365 /**
366 * Traitement des raccourcis de tableaux
367 *
368 * @param string $bloc
369 * @return string
370 */
371 function traiter_tableau($bloc) {
372 // id "unique" pour les id du tableau
373 $tabid = substr(md5($bloc), 0, 4);
374
375 // Decouper le tableau en lignes
376 preg_match_all(',([|].*)[|]\n,UmsS', $bloc, $regs, PREG_PATTERN_ORDER);
377 $lignes = array();
378 $debut_table = $summary = '';
379 $l = 0;
380 $numeric = true;
381
382 // Traiter chaque ligne
383 $reg_line1 = ',^(\|(' . _RACCOURCI_TH_SPAN . '))+$,sS';
384 $reg_line_all = ',^(' . _RACCOURCI_TH_SPAN . ')$,sS';
385 $hc = $hl = array();
386 $thead_ok = false;
387 foreach ($regs[1] as $ligne) {
388 $l++;
389
390 // Gestion de la premiere ligne :
391 if (!$thead_ok and $l == 1) {
392 // - <caption> et summary dans la premiere ligne (seulement si on n'a pas dépassé le premier thead) :
393 // || caption | summary || (|summary est optionnel)
394 if (preg_match(',^\|\|([^|]*)(\|(.*))?$,sS', rtrim($ligne, '|'), $cap)) {
395 $cap = array_pad($cap, 4, null);
396 $l = 0;
397 if ($caption = trim($cap[1])) {
398 $debut_table .= "<caption>" . $caption . "</caption>\n";
399 }
400 $summary = ' summary="' . entites_html(trim($cap[3])) . '"';
401 }
402 // - <thead> sous la forme |{{titre}}|{{titre}}|
403 // Attention thead oblige a avoir tbody
404 else {
405 if (preg_match($reg_line1, $ligne, $thead)) {
406 preg_match_all('/\|([^|]*)/S', $ligne, $cols);
407 $ligne = '';
408 $cols = $cols[1];
409 $colspan = 1;
410 for ($c = count($cols) - 1; $c >= 0; $c--) {
411 $attr = '';
412 if ($cols[$c] == '<') {
413 $colspan++;
414 } else {
415 if ($colspan > 1) {
416 $attr = " colspan='$colspan'";
417 $colspan = 1;
418 }
419 // inutile de garder le strong qui n'a servi que de marqueur
420 $cols[$c] = str_replace(array('{', '}'), '', $cols[$c]);
421 $ligne = "<th id='id{$tabid}_c$c'$attr>$cols[$c]</th>$ligne";
422 $hc[$c] = "id{$tabid}_c$c"; // pour mettre dans les headers des td
423 }
424 }
425
426 $debut_table .= "<thead><tr class='row_first'>" .
427 $ligne . "</tr></thead>\n";
428 $l = 0;
429 $thead_ok = true;
430 }
431 }
432 }
433
434 // Sinon ligne normale
435 if ($l) {
436 // Gerer les listes a puce dans les cellules
437 // on declenche simplement sur \n- car il y a les
438 // -* -# -? -! (qui produisent des -&nbsp;!)
439 if (strpos($ligne, "\n-") !== false) {
440 $ligne = traiter_listes($ligne);
441 }
442
443 // tout mettre dans un tableau 2d
444 preg_match_all('/\|([^|]*)/S', $ligne, $cols);
445
446 // Pas de paragraphes dans les cellules
447 foreach ($cols[1] as &$col) {
448 if (strlen($col = trim($col))) {
449 $col = preg_replace("/\n{2,}/S", "<br /> <br />", $col);
450 if (_AUTOBR) {
451 $col = str_replace("\n", _AUTOBR . "\n", $col);
452 }
453 }
454 }
455
456 // assembler le tableau
457 $lignes[] = $cols[1];
458 }
459 }
460
461 // maintenant qu'on a toutes les cellules
462 // on prepare une liste de rowspan par defaut, a partir
463 // du nombre de colonnes dans la premiere ligne.
464 // Reperer egalement les colonnes numeriques pour les cadrer a droite
465 $rowspans = $numeric = array();
466 $k = count($lignes);
467 $n = $k ? count($lignes[0]) : 0;
468
469 // distinguer les colonnes numeriques a point ou a virgule,
470 // pour les alignements eventuels sur "," ou "."
471 $numeric_class = array(
472 '.' => 'point',
473 ',' => 'virgule',
474 true => ''
475 );
476 for ($i = 0; $i < $n; $i++) {
477 $align = true;
478 for ($j = 0; $j < $k; $j++) {
479 $rowspans[$j][$i] = 1;
480 if ($align and preg_match('/^[{+-]*(?:\s|\d)*([.,]?)\d*[}]*$/', trim($lignes[$j][$i]), $r)) {
481 if ($r[1]) {
482 $align = $r[1];
483 }
484 } else {
485 $align = '';
486 }
487 }
488 $numeric[$i] = $align ? (" class='numeric " . $numeric_class[$align] . "'") : '';
489 }
490 for ($j = 0; $j < $k; $j++) {
491 if (preg_match($reg_line_all, $lignes[$j][0])) {
492 $hl[$j] = "id{$tabid}_l$j"; // pour mettre dans les headers des td
493 } else {
494 unset($hl[0]);
495 }
496 }
497 if (!isset($hl[0])) {
498 $hl = array();
499 } // toute la colonne ou rien
500
501 // et on parcourt le tableau a l'envers pour ramasser les
502 // colspan et rowspan en passant
503 $html = '';
504
505 for ($l = count($lignes) - 1; $l >= 0; $l--) {
506 $cols = $lignes[$l];
507 $colspan = 1;
508 $ligne = '';
509
510 for ($c = count($cols) - 1; $c >= 0; $c--) {
511 $attr = $numeric[$c];
512 $cell = trim($cols[$c]);
513 if ($cell == '<') {
514 $colspan++;
515
516 } elseif ($cell == '^') {
517 $rowspans[$l - 1][$c] += $rowspans[$l][$c];
518
519 } else {
520 if ($colspan > 1) {
521 $attr .= " colspan='$colspan'";
522 $colspan = 1;
523 }
524 if (($x = $rowspans[$l][$c]) > 1) {
525 $attr .= " rowspan='$x'";
526 }
527 $b = ($c == 0 and isset($hl[$l])) ? 'th' : 'td';
528 $h = (isset($hc[$c]) ? $hc[$c] : '') . ' ' . (($b == 'td' and isset($hl[$l])) ? $hl[$l] : '');
529 if ($h = trim($h)) {
530 $attr .= " headers='$h'";
531 }
532 // inutile de garder le strong qui n'a servi que de marqueur
533 if ($b == 'th') {
534 $attr .= " id='" . $hl[$l] . "'";
535 $cols[$c] = str_replace(array('{', '}'), '', $cols[$c]);
536 }
537 $ligne = "\n<$b" . $attr . '>' . $cols[$c] . "</$b>" . $ligne;
538 }
539 }
540
541 // ligne complete
542 $class = alterner($l + 1, 'odd', 'even');
543 $html = "<tr class='row_$class $class'>$ligne</tr>\n$html";
544 }
545
546 return "\n\n<table" . $GLOBALS['class_spip_plus'] . $summary . ">\n"
547 . $debut_table
548 . "<tbody>\n"
549 . $html
550 . "</tbody>\n"
551 . "</table>\n\n";
552 }
553
554
555 /**
556 * Traitement des listes
557 *
558 * On utilise la wheel correspondante
559 *
560 * @param string $t
561 * @return string
562 */
563 function traiter_listes($t) {
564 static $wheel = null;
565
566 if (!isset($wheel)) {
567 $wheel = new TextWheel(
568 SPIPTextWheelRuleset::loader($GLOBALS['spip_wheels']['listes'])
569 );
570 }
571
572 try {
573 $t = $wheel->text($t);
574 } catch (Exception $e) {
575 erreur_squelette($e->getMessage());
576 }
577
578 return $t;
579 }
580
581
582 // Ces deux constantes permettent de proteger certains caracteres
583 // en les remplacanat par des caracteres "illegaux". (cf corriger_caracteres)
584
585 define('_RACCOURCI_PROTEGER', "{}_-");
586 define('_RACCOURCI_PROTECTEUR', "\x1\x2\x3\x4");
587
588 define('_RACCOURCI_BALISE', ",</?[a-z!][^<>]*[" . preg_quote(_RACCOURCI_PROTEGER) . "][^<>]*>,imsS");
589
590 /**
591 * mais d'abord, une callback de reconfiguration des raccourcis
592 * a partir de globales (est-ce old-style ? on conserve quand meme
593 * par souci de compat ascendante)
594 *
595 * @param $ruleset
596 * @return string
597 */
598 function personnaliser_raccourcis(&$ruleset) {
599 if ($ruleset) {
600 if (isset($GLOBALS['debut_intertitre']) and $rule = $ruleset->getRule('intertitres')) {
601 $rule->replace[0] = preg_replace(',<[^>]*>,Uims', $GLOBALS['debut_intertitre'], $rule->replace[0]);
602 $rule->replace[1] = preg_replace(',<[^>]*>,Uims', $GLOBALS['fin_intertitre'], $rule->replace[1]);
603 $ruleset->addRules(array('intertitres' => $rule));
604 }
605 if (isset($GLOBALS['debut_gras']) and $rule = $ruleset->getRule('gras')) {
606 $rule->replace[0] = preg_replace(',<[^>]*>,Uims', $GLOBALS['debut_gras'], $rule->replace[0]);
607 $rule->replace[1] = preg_replace(',<[^>]*>,Uims', $GLOBALS['fin_gras'], $rule->replace[1]);
608 $ruleset->addRules(array('gras' => $rule));
609 }
610 if (isset($GLOBALS['debut_italique']) and $rule = $ruleset->getRule('italiques')) {
611 $rule->replace[0] = preg_replace(',<[^>]*>,Uims', $GLOBALS['debut_italique'], $rule->replace[0]);
612 $rule->replace[1] = preg_replace(',<[^>]*>,Uims', $GLOBALS['fin_italique'], $rule->replace[1]);
613 $ruleset->addRules(array('italiques' => $rule));
614 }
615 if (isset($GLOBALS['ligne_horizontale']) and $rule = $ruleset->getRule('ligne-horizontale')) {
616 $rule->replace = preg_replace(',<[^>]*>,Uims', $GLOBALS['ligne_horizontale'], $rule->replace);
617 $ruleset->addRules(array('ligne-horizontale' => $rule));
618 }
619 if (isset($GLOBALS['toujours_paragrapher']) and !$GLOBALS['toujours_paragrapher']
620 and $rule = $ruleset->getRule('toujours-paragrapher')
621 ) {
622 $rule->disabled = true;
623 $ruleset->addRules(array('toujours-paragrapher' => $rule));
624 }
625 }
626
627 // retourner une signature de l'etat de la fonction, pour la mise en cache
628 return implode("/",
629 array(
630 isset($GLOBALS['debut_intertitre']) ? $GLOBALS['debut_intertitre'] : "",
631 isset($GLOBALS['debut_gras']) ? $GLOBALS['debut_gras'] : "",
632 isset($GLOBALS['debut_italique']) ? $GLOBALS['debut_italique'] : "",
633 isset($GLOBALS['ligne_horizontale']) ? $GLOBALS['ligne_horizontale'] : "",
634 isset($GLOBALS['toujours_paragrapher']) ? $GLOBALS['toujours_paragrapher'] : 1,
635 )
636 );
637 }
638
639 /**
640 * Nettoie un texte, traite les raccourcis autre qu'URL, la typo, etc.
641 *
642 * @pipeline_appel pre_propre
643 * @pipeline_appel post_propre
644 *
645 * @param string $t
646 * @param bool $show_autobr
647 * @return string
648 */
649 function traiter_raccourcis($t, $show_autobr = false) {
650 static $wheel = array(), $notes;
651 static $img_br_auto, $img_br_manuel, $img_br_no;
652 global $spip_lang, $spip_lang_rtl;
653
654 // hack1: respecter le tag ignore br
655 if (_AUTOBR_IGNORER
656 and strncmp($t, _AUTOBR_IGNORER, strlen(_AUTOBR_IGNORER)) == 0
657 ) {
658 $ignorer_autobr = true;
659 $t = substr($t, strlen(_AUTOBR_IGNORER));
660 } else {
661 $ignorer_autobr = false;
662 }
663
664 // Appeler les fonctions de pre_traitement
665 $t = pipeline('pre_propre', $t);
666
667 $key = "";
668 $key = personnaliser_raccourcis($key);
669 if (!isset($wheel[$key])) {
670 $ruleset = SPIPTextWheelRuleset::loader(
671 $GLOBALS['spip_wheels']['raccourcis'], 'personnaliser_raccourcis'
672 );
673 $wheel[$key] = new TextWheel($ruleset);
674
675 if (_request('var_mode') == 'wheel'
676 and autoriser('debug')
677 ) {
678 $f = $wheel->compile();
679 echo "<pre>\n" . spip_htmlspecialchars($f) . "</pre>\n";
680 exit;
681 }
682 $notes = charger_fonction('notes', 'inc');
683 }
684
685 // Gerer les notes (ne passe pas dans le pipeline)
686 list($t, $mes_notes) = $notes($t);
687
688 try {
689 $t = $wheel[$key]->text($t);
690 } catch (Exception $e) {
691 erreur_squelette($e->getMessage());
692 }
693
694 // Appeler les fonctions de post-traitement
695 $t = pipeline('post_propre', $t);
696
697 if ($mes_notes) {
698 $notes($mes_notes, 'traiter', $ignorer_autobr);
699 }
700
701 if (_AUTOBR and !function_exists('aide_lang_dir')) {
702 include_spip('inc/lang');
703 }
704
705 // hack2: wrap des autobr dans l'espace prive, pour affichage css
706 // car en css on ne sait pas styler l'element BR
707 if ($ignorer_autobr and _AUTOBR) {
708 if (is_null($img_br_no)) {
709 $img_br_no = ($show_autobr ? http_img_pack("br-no" . aide_lang_dir($spip_lang, $spip_lang_rtl) . "-10.png",
710 _T("tw:retour_ligne_ignore"), "class='br-no'", _T("tw:retour_ligne_ignore")) : "");
711 }
712 $t = str_replace(_AUTOBR, $img_br_no, $t);
713 }
714 if ($show_autobr and _AUTOBR) {
715 if (is_null($img_br_manuel)) {
716 $img_br_manuel = http_img_pack("br-manuel" . aide_lang_dir($spip_lang, $spip_lang_rtl) . "-10.png",
717 _T("tw:retour_ligne_manuel"), "class='br-manuel'", _T("tw:retour_ligne_manuel"));
718 }
719 if (is_null($img_br_auto)) {
720 $img_br_auto = http_img_pack("br-auto" . aide_lang_dir($spip_lang, $spip_lang_rtl) . "-10.png",
721 _T("tw:retour_ligne_auto"), "class='br-auto'", _T("tw:retour_ligne_auto"));
722 }
723 if (false !== strpos(strtolower($t), '<br')) {
724 $t = preg_replace("/<br\b.*>/UiS", "$img_br_manuel\\0", $t);
725 $t = str_replace($img_br_manuel . _AUTOBR, $img_br_auto . _AUTOBR, $t);
726 }
727 }
728
729 return $t;
730 }
731
732
733 /**
734 * Transforme les raccourcis SPIP, liens et modèles d'un texte en code HTML
735 *
736 * Filtre à appliquer aux champs du type `#TEXTE*`
737 *
738 * @filtre
739 * @uses echappe_html()
740 * @uses expanser_liens()
741 * @uses traiter_raccourcis()
742 * @uses echappe_retour_modeles()
743 * @see typo()
744 *
745 * @param string $t
746 * Texte avec des raccourcis SPIP
747 * @param string|null $connect
748 * Nom du connecteur à la bdd
749 * @param array $env
750 * Environnement (pour les calculs de modèles)
751 * @return string $t
752 * Texte transformé
753 **/
754 function propre($t, $connect = null, $env = array()) {
755 // les appels directs a cette fonction depuis le php de l'espace
756 // prive etant historiquement ecrits sans argment $connect
757 // on utilise la presence de celui-ci pour distinguer les cas
758 // ou il faut passer interdire_script explicitement
759 // les appels dans les squelettes (de l'espace prive) fournissant un $connect
760 // ne seront pas perturbes
761 $interdire_script = false;
762 if (is_null($connect) and test_espace_prive()) {
763 $connect = '';
764 $interdire_script = true;
765 }
766
767 if (!$t) {
768 return strval($t);
769 }
770
771 $t = pipeline('pre_echappe_html_propre', $t);
772
773 // Dans l'espace prive on se mefie de tout contenu dangereux
774 // avant echappement des balises <html>
775 // https://core.spip.net/issues/3371
776 // et aussi dans l'espace public si la globale filtrer_javascript = -1
777 // https://core.spip.net/issues/4166
778 if ($interdire_script
779 or $GLOBALS['filtrer_javascript'] == -1
780 or (isset($env['espace_prive']) and $env['espace_prive'] and $GLOBALS['filtrer_javascript']<=0)
781 or (isset($env['wysiwyg']) and $env['wysiwyg'] and $GLOBALS['filtrer_javascript']<=0)) {
782 $t = echapper_html_suspect($t, false);
783 }
784 $t = echappe_html($t);
785 $t = expanser_liens($t, $connect, $env);
786
787 $t = traiter_raccourcis($t, (isset($env['wysiwyg']) and $env['wysiwyg']) ? true : false);
788 $t = echappe_retour_modeles($t, $interdire_script);
789
790 $t = pipeline('post_echappe_html_propre', $t);
791
792 return $t;
793 }