[SPIP] ~maj v3.2.9-->v3.2.11
[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
381 // Traiter chaque ligne
382 $reg_line1 = ',^(\|(' . _RACCOURCI_TH_SPAN . '))+$,sS';
383 $reg_line_all = ',^(' . _RACCOURCI_TH_SPAN . ')$,sS';
384 $hc = $hl = array();
385 $thead_ok = false;
386 foreach ($regs[1] as $ligne) {
387 $l++;
388
389 // Gestion de la premiere ligne :
390 if (!$thead_ok and $l == 1) {
391 // - <caption> et summary dans la premiere ligne (seulement si on n'a pas dépassé le premier thead) :
392 // || caption | summary || (|summary est optionnel)
393 if (preg_match(',^\|\|([^|]*)(\|(.*))?$,sS', rtrim($ligne, '|'), $cap)) {
394 $cap = array_pad($cap, 4, null);
395 $l = 0;
396 if ($caption = trim($cap[1])) {
397 $debut_table .= "<caption>" . $caption . "</caption>\n";
398 }
399 $summary = ' summary="' . entites_html(trim($cap[3])) . '"';
400 }
401 // - <thead> sous la forme |{{titre}}|{{titre}}|
402 // Attention thead oblige a avoir tbody
403 else {
404 if (preg_match($reg_line1, $ligne, $thead)) {
405 preg_match_all('/\|([^|]*)/S', $ligne, $cols);
406 $ligne = '';
407 $cols = $cols[1];
408 $colspan = 1;
409 for ($c = count($cols) - 1; $c >= 0; $c--) {
410 $attr = '';
411 if ($cols[$c] == '<') {
412 $colspan++;
413 } else {
414 if ($colspan > 1) {
415 $attr = " colspan='$colspan'";
416 $colspan = 1;
417 }
418 // inutile de garder le strong qui n'a servi que de marqueur
419 $cols[$c] = str_replace(array('{', '}'), '', $cols[$c]);
420 $ligne = "<th id='id{$tabid}_c$c'$attr>$cols[$c]</th>$ligne";
421 $hc[$c] = "id{$tabid}_c$c"; // pour mettre dans les headers des td
422 }
423 }
424
425 $debut_table .= "<thead><tr class='row_first'>" .
426 $ligne . "</tr></thead>\n";
427 $l = 0;
428 $thead_ok = true;
429 }
430 }
431 }
432
433 // Sinon ligne normale
434 if ($l) {
435 // Gerer les listes a puce dans les cellules
436 // on declenche simplement sur \n- car il y a les
437 // -* -# -? -! (qui produisent des -&nbsp;!)
438 if (strpos($ligne, "\n-") !== false) {
439 $ligne = traiter_listes($ligne);
440 }
441
442 // tout mettre dans un tableau 2d
443 preg_match_all('/\|([^|]*)/S', $ligne, $cols);
444
445 // Pas de paragraphes dans les cellules
446 foreach ($cols[1] as &$col) {
447 if (strlen($col = trim($col))) {
448 $col = preg_replace("/\n{2,}/S", "<br /> <br />", $col);
449 if (_AUTOBR) {
450 $col = str_replace("\n", _AUTOBR . "\n", $col);
451 }
452 }
453 }
454
455 // assembler le tableau
456 $lignes[] = $cols[1];
457 }
458 }
459
460 // maintenant qu'on a toutes les cellules
461 // on prepare une liste de rowspan par defaut, a partir
462 // du nombre de colonnes dans la premiere ligne.
463 // Reperer egalement les colonnes numeriques pour les cadrer a droite
464 $rowspans = $numeric = array();
465 $k = count($lignes);
466 $n = $k ? count($lignes[0]) : 0;
467
468 // distinguer les colonnes numeriques a point ou a virgule,
469 // pour les alignements eventuels sur "," ou "."
470 $numeric_class = array(
471 '.' => 'point',
472 ',' => 'virgule',
473 true => ''
474 );
475 for ($i = 0; $i < $n; $i++) {
476 $align = true;
477 for ($j = 0; $j < $k; $j++) {
478 $rowspans[$j][$i] = 1;
479 if ($align and preg_match('/^[{+-]*(?:\s|\d)*([.,]?)\d*[}]*$/', trim($lignes[$j][$i]), $r)) {
480 if ($r[1]) {
481 $align = $r[1];
482 }
483 } else {
484 $align = '';
485 }
486 }
487 $numeric[$i] = $align ? (" class='numeric " . $numeric_class[$align] . "'") : '';
488 }
489 for ($j = 0; $j < $k; $j++) {
490 if (preg_match($reg_line_all, $lignes[$j][0])) {
491 $hl[$j] = "id{$tabid}_l$j"; // pour mettre dans les headers des td
492 } else {
493 unset($hl[0]);
494 }
495 }
496 if (!isset($hl[0])) {
497 $hl = array();
498 } // toute la colonne ou rien
499
500 // et on parcourt le tableau a l'envers pour ramasser les
501 // colspan et rowspan en passant
502 $html = '';
503
504 for ($l = count($lignes) - 1; $l >= 0; $l--) {
505 $cols = $lignes[$l];
506 $colspan = 1;
507 $ligne = '';
508
509 for ($c = count($cols) - 1; $c >= 0; $c--) {
510 $attr = $numeric[$c];
511 $cell = trim($cols[$c]);
512 if ($cell == '<') {
513 $colspan++;
514
515 } elseif ($cell == '^') {
516 $rowspans[$l - 1][$c] += $rowspans[$l][$c];
517
518 } else {
519 if ($colspan > 1) {
520 $attr .= " colspan='$colspan'";
521 $colspan = 1;
522 }
523 if (($x = $rowspans[$l][$c]) > 1) {
524 $attr .= " rowspan='$x'";
525 }
526 $b = ($c == 0 and isset($hl[$l])) ? 'th' : 'td';
527 $h = (isset($hc[$c]) ? $hc[$c] : '') . ' ' . (($b == 'td' and isset($hl[$l])) ? $hl[$l] : '');
528 if ($h = trim($h)) {
529 $attr .= " headers='$h'";
530 }
531 // inutile de garder le strong qui n'a servi que de marqueur
532 if ($b == 'th') {
533 $attr .= " id='" . $hl[$l] . "'";
534 $cols[$c] = str_replace(array('{', '}'), '', $cols[$c]);
535 }
536 $ligne = "\n<$b" . $attr . '>' . $cols[$c] . "</$b>" . $ligne;
537 }
538 }
539
540 // ligne complete
541 $class = alterner($l + 1, 'odd', 'even');
542 $html = "<tr class='row_$class $class'>$ligne</tr>\n$html";
543 }
544
545 return "\n\n<table" . $GLOBALS['class_spip_plus'] . $summary . ">\n"
546 . $debut_table
547 . "<tbody>\n"
548 . $html
549 . "</tbody>\n"
550 . "</table>\n\n";
551 }
552
553
554 /**
555 * Traitement des listes
556 *
557 * On utilise la wheel correspondante
558 *
559 * @param string $t
560 * @return string
561 */
562 function traiter_listes($t) {
563 static $wheel = null;
564
565 if (!isset($wheel)) {
566 $wheel = new TextWheel(
567 SPIPTextWheelRuleset::loader($GLOBALS['spip_wheels']['listes'])
568 );
569 }
570
571 try {
572 $t = $wheel->text($t);
573 } catch (Exception $e) {
574 erreur_squelette($e->getMessage());
575 }
576
577 return $t;
578 }
579
580
581 // Ces deux constantes permettent de proteger certains caracteres
582 // en les remplacanat par des caracteres "illegaux". (cf corriger_caracteres)
583
584 define('_RACCOURCI_PROTEGER', "{}_-");
585 define('_RACCOURCI_PROTECTEUR', "\x1\x2\x3\x4");
586
587 define('_RACCOURCI_BALISE', ",</?[a-z!][^<>]*[" . preg_quote(_RACCOURCI_PROTEGER) . "][^<>]*>,imsS");
588
589 /**
590 * mais d'abord, une callback de reconfiguration des raccourcis
591 * a partir de globales (est-ce old-style ? on conserve quand meme
592 * par souci de compat ascendante)
593 *
594 * @param $ruleset
595 * @return string
596 */
597 function personnaliser_raccourcis(&$ruleset) {
598 if ($ruleset) {
599 if (isset($GLOBALS['debut_intertitre']) and $rule = $ruleset->getRule('intertitres')) {
600 $rule->replace[0] = preg_replace(',<[^>]*>,Uims', $GLOBALS['debut_intertitre'], $rule->replace[0]);
601 $rule->replace[1] = preg_replace(',<[^>]*>,Uims', $GLOBALS['fin_intertitre'], $rule->replace[1]);
602 $ruleset->addRules(array('intertitres' => $rule));
603 }
604 if (isset($GLOBALS['debut_gras']) and $rule = $ruleset->getRule('gras')) {
605 $rule->replace[0] = preg_replace(',<[^>]*>,Uims', $GLOBALS['debut_gras'], $rule->replace[0]);
606 $rule->replace[1] = preg_replace(',<[^>]*>,Uims', $GLOBALS['fin_gras'], $rule->replace[1]);
607 $ruleset->addRules(array('gras' => $rule));
608 }
609 if (isset($GLOBALS['debut_italique']) and $rule = $ruleset->getRule('italiques')) {
610 $rule->replace[0] = preg_replace(',<[^>]*>,Uims', $GLOBALS['debut_italique'], $rule->replace[0]);
611 $rule->replace[1] = preg_replace(',<[^>]*>,Uims', $GLOBALS['fin_italique'], $rule->replace[1]);
612 $ruleset->addRules(array('italiques' => $rule));
613 }
614 if (isset($GLOBALS['ligne_horizontale']) and $rule = $ruleset->getRule('ligne-horizontale')) {
615 $rule->replace = preg_replace(',<[^>]*>,Uims', $GLOBALS['ligne_horizontale'], $rule->replace);
616 $ruleset->addRules(array('ligne-horizontale' => $rule));
617 }
618 if (isset($GLOBALS['toujours_paragrapher']) and !$GLOBALS['toujours_paragrapher']
619 and $rule = $ruleset->getRule('toujours-paragrapher')
620 ) {
621 $rule->disabled = true;
622 $ruleset->addRules(array('toujours-paragrapher' => $rule));
623 }
624 }
625
626 // retourner une signature de l'etat de la fonction, pour la mise en cache
627 return implode("/",
628 array(
629 isset($GLOBALS['debut_intertitre']) ? $GLOBALS['debut_intertitre'] : "",
630 isset($GLOBALS['debut_gras']) ? $GLOBALS['debut_gras'] : "",
631 isset($GLOBALS['debut_italique']) ? $GLOBALS['debut_italique'] : "",
632 isset($GLOBALS['ligne_horizontale']) ? $GLOBALS['ligne_horizontale'] : "",
633 isset($GLOBALS['toujours_paragrapher']) ? $GLOBALS['toujours_paragrapher'] : 1,
634 )
635 );
636 }
637
638 /**
639 * Nettoie un texte, traite les raccourcis autre qu'URL, la typo, etc.
640 *
641 * @pipeline_appel pre_propre
642 * @pipeline_appel post_propre
643 *
644 * @param string $t
645 * @param bool $show_autobr
646 * @return string
647 */
648 function traiter_raccourcis($t, $show_autobr = false) {
649 static $wheel = array(), $notes;
650 static $img_br_auto, $img_br_manuel, $img_br_no;
651 global $spip_lang, $spip_lang_rtl;
652
653 // hack1: respecter le tag ignore br
654 if (_AUTOBR_IGNORER
655 and strncmp($t, _AUTOBR_IGNORER, strlen(_AUTOBR_IGNORER)) == 0
656 ) {
657 $ignorer_autobr = true;
658 $t = substr($t, strlen(_AUTOBR_IGNORER));
659 } else {
660 $ignorer_autobr = false;
661 }
662
663 // Appeler les fonctions de pre_traitement
664 $t = pipeline('pre_propre', $t);
665
666 $key = "";
667 $key = personnaliser_raccourcis($key);
668 if (!isset($wheel[$key])) {
669 $ruleset = SPIPTextWheelRuleset::loader(
670 $GLOBALS['spip_wheels']['raccourcis'], 'personnaliser_raccourcis'
671 );
672 $wheel[$key] = new TextWheel($ruleset);
673
674 if (_request('var_mode') == 'wheel'
675 and autoriser('debug')
676 ) {
677 $f = $wheel->compile();
678 echo "<pre>\n" . spip_htmlspecialchars($f) . "</pre>\n";
679 exit;
680 }
681 $notes = charger_fonction('notes', 'inc');
682 }
683
684 // Gerer les notes (ne passe pas dans le pipeline)
685 list($t, $mes_notes) = $notes($t);
686
687 try {
688 $t = $wheel[$key]->text($t);
689 } catch (Exception $e) {
690 erreur_squelette($e->getMessage());
691 }
692
693 // Appeler les fonctions de post-traitement
694 $t = pipeline('post_propre', $t);
695
696 if ($mes_notes) {
697 $notes($mes_notes, 'traiter', $ignorer_autobr);
698 }
699
700 if (_AUTOBR and !function_exists('aide_lang_dir')) {
701 include_spip('inc/lang');
702 }
703
704 // hack2: wrap des autobr dans l'espace prive, pour affichage css
705 // car en css on ne sait pas styler l'element BR
706 if ($ignorer_autobr and _AUTOBR) {
707 if (is_null($img_br_no)) {
708 $img_br_no = ($show_autobr ? http_img_pack("br-no" . aide_lang_dir($spip_lang, $spip_lang_rtl) . "-10.png",
709 _T("tw:retour_ligne_ignore"), "class='br-no'", _T("tw:retour_ligne_ignore")) : "");
710 }
711 $t = str_replace(_AUTOBR, $img_br_no, $t);
712 }
713 if ($show_autobr and _AUTOBR) {
714 if (is_null($img_br_manuel)) {
715 $img_br_manuel = http_img_pack("br-manuel" . aide_lang_dir($spip_lang, $spip_lang_rtl) . "-10.png",
716 _T("tw:retour_ligne_manuel"), "class='br-manuel'", _T("tw:retour_ligne_manuel"));
717 }
718 if (is_null($img_br_auto)) {
719 $img_br_auto = http_img_pack("br-auto" . aide_lang_dir($spip_lang, $spip_lang_rtl) . "-10.png",
720 _T("tw:retour_ligne_auto"), "class='br-auto'", _T("tw:retour_ligne_auto"));
721 }
722 if (false !== strpos(strtolower($t), '<br')) {
723 $t = preg_replace("/<br\b.*>/UiS", "$img_br_manuel\\0", $t);
724 $t = str_replace($img_br_manuel . _AUTOBR, $img_br_auto . _AUTOBR, $t);
725 }
726 }
727
728 return $t;
729 }
730
731
732 /**
733 * Transforme les raccourcis SPIP, liens et modèles d'un texte en code HTML
734 *
735 * Filtre à appliquer aux champs du type `#TEXTE*`
736 *
737 * @filtre
738 * @uses echappe_html()
739 * @uses expanser_liens()
740 * @uses traiter_raccourcis()
741 * @uses echappe_retour_modeles()
742 * @see typo()
743 *
744 * @param string $t
745 * Texte avec des raccourcis SPIP
746 * @param string|null $connect
747 * Nom du connecteur à la bdd
748 * @param array $env
749 * Environnement (pour les calculs de modèles)
750 * @return string $t
751 * Texte transformé
752 **/
753 function propre($t, $connect = null, $env = array()) {
754 // les appels directs a cette fonction depuis le php de l'espace
755 // prive etant historiquement ecrits sans argment $connect
756 // on utilise la presence de celui-ci pour distinguer les cas
757 // ou il faut passer interdire_script explicitement
758 // les appels dans les squelettes (de l'espace prive) fournissant un $connect
759 // ne seront pas perturbes
760 $interdire_script = false;
761 if (is_null($connect) and test_espace_prive()) {
762 $connect = '';
763 $interdire_script = true;
764 }
765
766 if (!$t) {
767 return strval($t);
768 }
769
770 $t = pipeline('pre_echappe_html_propre', $t);
771
772 // Dans l'espace prive on se mefie de tout contenu dangereux
773 // avant echappement des balises <html>
774 // https://core.spip.net/issues/3371
775 // et aussi dans l'espace public si la globale filtrer_javascript = -1
776 // https://core.spip.net/issues/4166
777 if ($interdire_script
778 or $GLOBALS['filtrer_javascript'] == -1
779 or (isset($env['espace_prive']) and $env['espace_prive'] and $GLOBALS['filtrer_javascript']<=0)
780 or (isset($env['wysiwyg']) and $env['wysiwyg'] and $GLOBALS['filtrer_javascript']<=0)) {
781 $t = echapper_html_suspect($t, false);
782 }
783 $t = echappe_html($t);
784 $t = expanser_liens($t, $connect, $env);
785
786 $t = traiter_raccourcis($t, (isset($env['wysiwyg']) and $env['wysiwyg']) ? true : false);
787 $t = echappe_retour_modeles($t, $interdire_script);
788
789 $t = pipeline('post_echappe_html_propre', $t);
790
791 return $t;
792 }