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