source: spip-zone/_plugins_/textwheel/inc/texte.php @ 58913

Last change on this file since 58913 was 58913, checked in by fil@…, 9 years ago

methode plus robuste pour afficher nos BR dans l'espace prive

File size: 30.0 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2010                                                *
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
13if (!defined("_ECRIRE_INC_VERSION")) return;
14
15include_spip('inc/filtres');
16include_spip('inc/lang');
17include_spip('inc/lien');
18
19include_spip('inc/textwheel');
20
21
22defined('_AUTOBR')||define('_AUTOBR', "<br class='autobr' />");
23
24// 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
25$GLOBALS['spip_raccourcis_typo'] = array();
26
27// Raccourcis dependant du sens de la langue
28function definir_raccourcis_alineas() {
29        global $ligne_horizontale;
30        static $alineas = array();
31        $x = _DIR_RESTREINT ? lang_dir() : lang_dir($GLOBALS['spip_lang']);
32        if (!isset($alineas[$x])) {
33
34                $alineas[$x] = array(
35                array(
36                /* 0 */         "/\n(----+|____+)/S",
37                /* 1 */         "/\n-- */S",
38                /* 2 */         "/\n- */S", /* DOIT rester a cette position */
39                /* 3 */         "/\n_ +/S"
40                                ),
41                array(
42                /* 0 */         "\n\n" . $ligne_horizontale . "\n\n",
43                /* 1 */         "\n<br />&mdash;&nbsp;",
44                /* 2 */         "\n<br />".definir_puce()."&nbsp;",
45                /* 3 */         "\n<br />"
46                                )
47                );
48        }
49        return $alineas[$x];
50}
51
52// On initialise la puce pour eviter find_in_path() a chaque rencontre de \n-
53// Mais attention elle depend de la direction et de X_fonctions.php, ainsi que
54// de l'espace choisi (public/prive)
55// http://doc.spip.org/@definir_puce
56function definir_puce() {
57
58        // Attention au sens, qui n'est pas defini de la meme facon dans
59        // l'espace prive (spip_lang est la langue de l'interface, lang_dir
60        // celle du texte) et public (spip_lang est la langue du texte)
61        $dir = _DIR_RESTREINT ? lang_dir() : lang_dir($GLOBALS['spip_lang']);
62
63        $p = 'puce' . (test_espace_prive() ? '_prive' : '');
64        if ($dir == 'rtl') $p .= '_rtl';
65
66        if (!isset($GLOBALS[$p])) {
67                $img = find_in_path($p.'.gif');
68                list(,,,$size) = @getimagesize($img);
69                $GLOBALS[$p] = '<img src="'.$img.'" '.$size.' class="puce" alt="-" />';
70        }
71        return $GLOBALS[$p];
72}
73
74// XHTML - Preserver les balises-bloc : on liste ici tous les elements
75// dont on souhaite qu'ils provoquent un saut de paragraphe
76define('_BALISES_BLOCS',
77        'div|pre|ul|ol|li|blockquote|h[1-6r]|'
78        .'t(able|[rdh]|body|foot|extarea)|'
79        .'form|object|center|marquee|address|'
80        .'d[ltd]|script|noscript|map|button|fieldset|style');
81
82//
83// Echapper les elements perilleux en les passant en base64
84//
85
86// Creer un bloc base64 correspondant a $rempl ; au besoin en marquant
87// une $source differente ; le script detecte automagiquement si ce qu'on
88// echappe est un div ou un span
89// http://doc.spip.org/@code_echappement
90function code_echappement($rempl, $source='', $no_transform=false) {
91        if (!strlen($rempl)) return '';
92
93        // Tester si on echappe en span ou en div
94        $mode = preg_match(',</?('._BALISES_BLOCS.')[>[:space:]],iS', $rempl) ?
95                'div' : 'span';
96        $return = '';
97
98        // Decouper en morceaux, base64 a des probleme selon la taille de la pile
99        $taille = 30000;
100        for($i = 0; $i < strlen($rempl); $i += $taille) {
101                // Convertir en base64 et cacher dans un attribut
102                // utiliser les " pour eviter le re-encodage de ' et &#8217
103                $base64 = base64_encode(substr($rempl, $i, $taille));
104                $return .= "<$mode class=\"base64$source\" title=\"$base64\"></$mode>";
105        }
106
107        return $return
108                . ((!$no_transform AND $mode == 'div')
109                        ? "\n\n"
110                        : ''
111                );
112;
113}
114
115// Echapper les <html>...</ html>
116// http://doc.spip.org/@traiter_echap_html_dist
117function traiter_echap_html_dist($regs) {
118        return $regs[3];
119}
120
121// Echapper les <code>...</ code>
122// http://doc.spip.org/@traiter_echap_code_dist
123function traiter_echap_code_dist($regs) {
124        list(,,$att,$corps) = $regs;
125        $echap = htmlspecialchars($corps); // il ne faut pas passer dans entites_html, ne pas transformer les &#xxx; du code !
126
127        // ne pas mettre le <div...> s'il n'y a qu'une ligne
128        if (is_int(strpos($echap,"\n"))) {
129                // supprimer les sauts de ligne debut/fin
130                // (mais pas les espaces => ascii art).
131                $echap = preg_replace("/^[\n\r]+|[\n\r]+$/s", "", $echap);
132                $echap = nl2br($echap);
133                $echap = "<div style='text-align: left;' "
134                . "class='spip_code' dir='ltr'><code$att>"
135                .$echap."</code></div>";
136        } else {
137                $echap = "<code$att class='spip_code' dir='ltr'>".$echap."</code>";
138        }
139
140        $echap = str_replace("\t", "&nbsp; &nbsp; &nbsp; &nbsp; ", $echap);
141        $echap = str_replace("  ", " &nbsp;", $echap);
142        return $echap;
143}
144
145// Echapper les <cadre>...</ cadre> aka <frame>...</ frame>
146// http://doc.spip.org/@traiter_echap_cadre_dist
147function traiter_echap_cadre_dist($regs) {
148        $echap = trim(entites_html($regs[3]));
149        // compter les lignes un peu plus finement qu'avec les \n
150        $lignes = explode("\n",trim($echap));
151        $n = 0;
152        foreach($lignes as $l)
153                $n+=floor(strlen($l)/60)+1;
154        $n = max($n,2);
155        $echap = "\n<textarea readonly='readonly' cols='40' rows='$n' class='spip_cadre' dir='ltr'>$echap</textarea>";
156        return generer_form_ecrire('', $echap, " method='get'");
157}
158// http://doc.spip.org/@traiter_echap_frame_dist
159function traiter_echap_frame_dist($regs) {
160        return traiter_echap_cadre_dist($regs);
161}
162
163// http://doc.spip.org/@traiter_echap_script_dist
164function traiter_echap_script_dist($regs) {
165        // rendre joli (et inactif) si c'est un script language=php
166        if (preg_match(',<script\b[^>]+php,ims', $regs[0]))
167                return highlight_string($regs[0],true);
168
169        // Cas normal : le script passe tel quel
170        return $regs[0];
171}
172
173define('_PROTEGE_BLOCS', ',<(html|code|cadre|frame|script)(\s[^>]*)?>(.*)</\1>,UimsS');
174
175// - pour $source voir commentaire infra (echappe_retour)
176// - pour $no_transform voir le filtre post_autobr dans inc/filtres
177// http://doc.spip.org/@echappe_html
178function echappe_html($letexte, $source='', $no_transform=false,
179$preg='') {
180        if (!is_string($letexte) or !strlen($letexte))
181                return $letexte;
182
183        if (($preg OR strpos($letexte,"<")!==false)
184          AND preg_match_all($preg ? $preg : _PROTEGE_BLOCS, $letexte, $matches, PREG_SET_ORDER))
185                foreach ($matches as $regs) {
186                        // echappements tels quels ?
187                        if ($no_transform) {
188                                $echap = $regs[0];
189                        }
190
191                        // sinon les traiter selon le cas
192                        else if (function_exists($f = 'traiter_echap_'.strtolower($regs[1])))
193                                $echap = $f($regs);
194                        else if (function_exists($f = $f.'_dist'))
195                                $echap = $f($regs);
196
197                        $letexte = str_replace($regs[0],
198                                code_echappement($echap, $source, $no_transform),
199                                $letexte);
200                }
201
202        if ($no_transform)
203                return $letexte;
204
205        // Gestion du TeX
206        if (strpos($letexte, "<math>") !== false) {
207                include_spip('inc/math');
208                $letexte = traiter_math($letexte, $source);
209        }
210
211        // Echapper le php pour faire joli (ici, c'est pas pour la securite)
212        if (strpos($letexte,"<"."?")!==false AND preg_match_all(',<[?].*($|[?]>),UisS',
213        $letexte, $matches, PREG_SET_ORDER))
214        foreach ($matches as $regs) {
215                $letexte = str_replace($regs[0],
216                        code_echappement(highlight_string($regs[0],true), $source),
217                        $letexte);
218        }
219
220        return $letexte;
221}
222
223//
224// Traitement final des echappements
225// Rq: $source sert a faire des echappements "a soi" qui ne sont pas nettoyes
226// par propre() : exemple dans multi et dans typo()
227// http://doc.spip.org/@echappe_retour
228function echappe_retour($letexte, $source='', $filtre = "") {
229        if (strpos($letexte,"base64$source")) {
230                # spip_log(htmlspecialchars($letexte));  ## pour les curieux
231                if (strpos($letexte,"<")!==false AND
232                  preg_match_all(',<(span|div) class=[\'"]base64'.$source.'[\'"]\s(.*)>\s*</\1>,UmsS',
233                $letexte, $regs, PREG_SET_ORDER)) {
234                        foreach ($regs as $reg) {
235                                $rempl = base64_decode(extraire_attribut($reg[0], 'title'));
236                                // recherche d'attributs supplementaires
237                                $at = array();
238                                foreach(array('lang', 'dir') as $attr) {
239                                        if ($a = extraire_attribut($reg[0], $attr))
240                                                $at[$attr] = $a;
241                                }
242                                if ($at) {
243                                        $rempl = '<'.$reg[1].'>'.$rempl.'</'.$reg[1].'>';
244                                        foreach($at as $attr => $a)
245                                                $rempl = inserer_attribut($rempl, $attr, $a);
246                                }
247                                if ($filtre) $rempl = $filtre($rempl);
248                                $letexte = str_replace($reg[0], $rempl, $letexte);
249                        }
250                }
251        }
252        return $letexte;
253}
254
255// Reinserer le javascript de confiance (venant des modeles)
256
257// http://doc.spip.org/@echappe_retour_modeles
258function echappe_retour_modeles($letexte, $interdire_scripts=false){
259        $letexte = echappe_retour($letexte);
260
261        // Dans les appels directs hors squelette, securiser aussi ici
262        if ($interdire_scripts)
263                $letexte = interdire_scripts($letexte,true);
264
265        return trim($letexte);
266}
267
268// http://doc.spip.org/@couper
269function couper($texte, $taille=50, $suite = '&nbsp;(...)') {
270        if (!($length=strlen($texte)) OR $taille <= 0) return '';
271        $offset = 400 + 2*$taille;
272        while ($offset<$length
273                AND strlen(preg_replace(",<[^>]+>,Uims","",substr($texte,0,$offset)))<$taille)
274                $offset = 2*$offset;
275        if (    $offset<$length
276                        && ($p_tag_ouvrant = strpos($texte,'<',$offset))!==NULL){
277                $p_tag_fermant = strpos($texte,'>',$offset);
278                if ($p_tag_fermant<$p_tag_ouvrant)
279                        $offset = $p_tag_fermant+1; // prolonger la coupe jusqu'au tag fermant suivant eventuel
280        }
281        $texte = substr($texte, 0, $offset); /* eviter de travailler sur 10ko pour extraire 150 caracteres */
282
283        // on utilise les \r pour passer entre les gouttes
284        $texte = str_replace("\r\n", "\n", $texte);
285        $texte = str_replace("\r", "\n", $texte);
286
287        // sauts de ligne et paragraphes
288        $texte = preg_replace("/\n\n+/", "\r", $texte);
289        $texte = preg_replace("/<(p|br)( [^>]*)?".">/", "\r", $texte);
290
291        // supprimer les traits, lignes etc
292        $texte = preg_replace("/(^|\r|\n)(-[-#\*]*|_ )/", "\r", $texte);
293
294        // supprimer les tags
295        $texte = supprimer_tags($texte);
296        $texte = trim(str_replace("\n"," ", $texte));
297        $texte .= "\n"; // marquer la fin
298
299        // travailler en accents charset
300        $texte = unicode2charset(html2unicode($texte, /* secure */ true));
301        $texte = nettoyer_raccourcis_typo($texte);
302
303        // corriger la longueur de coupe
304        // en fonction de la presence de caracteres utf
305        if ($GLOBALS['meta']['charset']=='utf-8'){
306                $long = charset2unicode($texte);
307                $long = spip_substr($long, 0, max($taille,1));
308                $nbcharutf = preg_match_all('/(&#[0-9]{3,5};)/S', $long, $matches);
309                $taille += $nbcharutf;
310        }
311
312
313        // couper au mot precedent
314        $long = spip_substr($texte, 0, max($taille-4,1));
315        $u = $GLOBALS['meta']['pcre_u'];
316        $court = preg_replace("/([^\s][\s]+)[^\s]*\n?$/".$u, "\\1", $long);
317        $points = $suite;
318
319        // trop court ? ne pas faire de (...)
320        if (spip_strlen($court) < max(0.75 * $taille,2)) {
321                $points = '';
322                $long = spip_substr($texte, 0, $taille);
323                $texte = preg_replace("/([^\s][\s]+)[^\s]*\n?$/".$u, "\\1", $long);
324                // encore trop court ? couper au caractere
325                if (spip_strlen($texte) < 0.75 * $taille)
326                        $texte = $long;
327        } else
328                $texte = $court;
329
330        if (strpos($texte, "\n"))       // la fin est encore la : c'est qu'on n'a pas de texte de suite
331                $points = '';
332
333        // remettre les paragraphes
334        $texte = preg_replace("/\r+/", "\n\n", $texte);
335
336        // supprimer l'eventuelle entite finale mal coupee
337        $texte = preg_replace('/&#?[a-z0-9]*$/S', '', $texte);
338
339        return quote_amp(trim($texte)).$points;
340}
341
342//
343// echapper les < script ...
344//
345function echappe_js($t) {
346        static $wheel = null;
347
348        if (!isset($wheel))
349                $wheel = new TextWheel(
350                        SPIPTextWheelRuleset::loader($GLOBALS['spip_wheels']['echappe_js'])
351                );
352
353        return $wheel->text($t);
354}
355
356
357// http://doc.spip.org/@protege_js_modeles
358function protege_js_modeles($t) {
359        if (isset($GLOBALS['visiteur_session'])){
360                if (preg_match_all(',<script.*?($|</script.),isS', $t, $r, PREG_SET_ORDER)){
361                        if (!defined('_PROTEGE_JS_MODELES')){
362                                include_spip('inc/acces');
363                                define('_PROTEGE_JS_MODELES',creer_uniqid());
364                        }
365                        foreach ($r as $regs)
366                                $t = str_replace($regs[0],code_echappement($regs[0],'javascript'._PROTEGE_JS_MODELES),$t);
367                }
368                if (preg_match_all(',<\?php.*?($|\?'.'>),isS', $t, $r, PREG_SET_ORDER)){
369                        if (!defined('_PROTEGE_PHP_MODELES')){
370                                include_spip('inc/acces');
371                                define('_PROTEGE_PHP_MODELES',creer_uniqid());
372                        }
373                        foreach ($r as $regs)
374                                $t = str_replace($regs[0],code_echappement($regs[0],'php'._PROTEGE_PHP_MODELES),$t);
375                }
376        }
377        return $t;
378}
379
380// Securite : empecher l'execution de code PHP, en le transformant en joli code
381// dans l'espace prive, cette fonction est aussi appelee par propre et typo
382// si elles sont appelees en direct
383// il ne faut pas desactiver globalement la fonction dans l'espace prive car elle protege
384// aussi les balises des squelettes qui ne passent pas forcement par propre ou typo apres
385// http://doc.spip.org/@interdire_scripts
386function interdire_scripts($arg) {
387        static $dejavu = array();
388        static $wheel = null;
389
390        // Attention, si ce n'est pas une chaine, laisser intact
391        if (!$arg OR !is_string($arg) OR !strstr($arg, '<')) return $arg;
392        if (isset($dejavu[$GLOBALS['filtrer_javascript']][$arg])) return $dejavu[$GLOBALS['filtrer_javascript']][$arg];
393
394        if (!isset($wheel)){
395                $ruleset = SPIPTextWheelRuleset::loader(
396                        $GLOBALS['spip_wheels']['interdire_scripts']
397                );
398                // Pour le js, trois modes : parano (-1), prive (0), ok (1)
399                // desactiver la regle echappe-js si besoin
400                if ($GLOBALS['filtrer_javascript']==1
401                        OR ($GLOBALS['filtrer_javascript']==0 AND !test_espace_prive()))
402                        $ruleset->addRules (array('securite-js'=>array('disabled'=>true)));
403                $wheel = new TextWheel($ruleset);
404        }
405
406        $t = $wheel->text($arg);
407
408        // Reinserer les echappements des modeles
409        if (defined('_PROTEGE_JS_MODELES'))
410                $t = echappe_retour($t,"javascript"._PROTEGE_JS_MODELES);
411        if (defined('_PROTEGE_PHP_MODELES'))
412                $t = echappe_retour($t,"php"._PROTEGE_PHP_MODELES);
413
414        return $dejavu[$GLOBALS['filtrer_javascript']][$arg] = $t;
415}
416
417
418// Securite : utiliser SafeHTML s'il est present dans ecrire/safehtml/
419// http://doc.spip.org/@safehtml
420function safehtml($t) {
421        static $safehtml;
422
423        # attention safehtml nettoie deux ou trois caracteres de plus. A voir
424        if (strpos($t,'<')===false)
425                return str_replace("\x00", '', $t);
426
427        $t = interdire_scripts($t); // jolifier le php
428        $t = echappe_js($t);
429
430        if (!isset($safehtml))
431                $safehtml = charger_fonction('safehtml', 'inc', true);
432        if ($safehtml)
433                $t = $safehtml($t);
434
435        return interdire_scripts($t); // interdire le php (2 precautions)
436}
437
438// Typographie generale
439// avec protection prealable des balises HTML et SPIP
440
441// http://doc.spip.org/@typo
442function typo($letexte, $echapper=true, $connect=null) {
443        // Plus vite !
444        if (!$letexte) return $letexte;
445
446        // les appels directs a cette fonction depuis le php de l'espace
447        // prive etant historiquement ecrit sans argment $connect
448        // on utilise la presence de celui-ci pour distinguer les cas
449        // ou il faut passer interdire_script explicitement
450        // les appels dans les squelettes (de l'espace prive) fournissant un $connect
451        // ne seront pas perturbes
452        $interdire_script = false;
453        if (is_null($connect)){
454                $connect = '';
455                $interdire_script = true;
456        }
457
458        // Echapper les codes <html> etc
459        if ($echapper)
460                $letexte = echappe_html($letexte, 'TYPO');
461
462        //
463        // Installer les modeles, notamment images et documents ;
464        //
465        // NOTE : propre() ne passe pas par ici mais directement par corriger_typo
466        // cf. inc/lien
467        $letexte = traiter_modeles($mem = $letexte, false, $echapper ? 'TYPO' : '', $connect);
468        if ($letexte != $mem) $echapper = true;
469        unset($mem);
470
471        $letexte = corriger_typo($letexte);
472
473        // reintegrer les echappements
474        if ($echapper)
475                $letexte = echappe_retour($letexte, 'TYPO');
476
477        // Dans les appels directs hors squelette, securiser ici aussi
478        if ($interdire_script)
479                $letexte = interdire_scripts($letexte);
480
481        return $letexte;
482}
483
484// Correcteur typographique
485
486define('_TYPO_PROTEGER', "!':;?~%-");
487define('_TYPO_PROTECTEUR', "\x1\x2\x3\x4\x5\x6\x7\x8");
488
489define('_TYPO_BALISE', ",</?[a-z!][^<>]*[".preg_quote(_TYPO_PROTEGER)."][^<>]*>,imsS");
490
491// http://doc.spip.org/@corriger_typo
492function corriger_typo($t, $lang='') {
493        // Plus vite !
494        if (!$t) return $t;
495
496        $t = pipeline('pre_typo', $t);
497
498        // Caracteres de controle "illegaux"
499        $t = corriger_caracteres($t);
500
501        // Proteger les caracteres typographiques a l'interieur des tags html
502        if (preg_match_all(_TYPO_BALISE, $t, $regs, PREG_SET_ORDER)) {
503                foreach ($regs as $reg) {
504                        $insert = $reg[0];
505                        // hack: on transforme les caracteres a proteger en les remplacant
506                        // par des caracteres "illegaux". (cf corriger_caracteres())
507                        $insert = strtr($insert, _TYPO_PROTEGER, _TYPO_PROTECTEUR);
508                        $t = str_replace($reg[0], $insert, $t);
509                }
510        }
511
512        // trouver les blocs multi et les traiter a part
513        $t = extraire_multi($e = $t, $lang, true);
514        $e = ($e === $t);
515
516        // Charger & appliquer les fonctions de typographie
517        $typographie = charger_fonction(lang_typo($lang), 'typographie');
518        $t = $typographie($t);
519
520        // Les citations en une autre langue, s'il y a lieu
521        if (!$e) $t = echappe_retour($t, 'multi');
522
523        // Retablir les caracteres proteges
524        $t = strtr($t, _TYPO_PROTECTEUR, _TYPO_PROTEGER);
525
526        // pipeline
527        $t = pipeline('post_typo', $t);
528
529        # un message pour abs_url - on est passe en mode texte
530        $GLOBALS['mode_abs_url'] = 'texte';
531
532        return $t;
533}
534
535
536//
537// Tableaux
538//
539
540define('_RACCOURCI_TH_SPAN', '\s*(?:{{[^{}]+}}\s*)?|<');
541
542// http://doc.spip.org/@traiter_tableau
543function traiter_tableau($bloc) {
544
545        // Decouper le tableau en lignes
546        preg_match_all(',([|].*)[|]\n,UmsS', $bloc, $regs, PREG_PATTERN_ORDER);
547        $lignes = array();
548        $debut_table = $summary = '';
549        $l = 0;
550        $numeric = true;
551
552        // Traiter chaque ligne
553        $reg_line1 = ',^(\|(' . _RACCOURCI_TH_SPAN . '))+$,sS';
554        $reg_line_all = ',^'  . _RACCOURCI_TH_SPAN . '$,sS';
555        foreach ($regs[1] as $ligne) {
556                $l ++;
557
558                // Gestion de la premiere ligne :
559                if ($l == 1) {
560                // - <caption> et summary dans la premiere ligne :
561                //   || caption | summary || (|summary est optionnel)
562                        if (preg_match(',^\|\|([^|]*)(\|(.*))?$,sS', rtrim($ligne,'|'), $cap)) {
563                                $l = 0;
564                                if ($caption = trim($cap[1]))
565                                        $debut_table .= "<caption>".$caption."</caption>\n";
566                                $summary = ' summary="'.entites_html(trim($cap[3])).'"';
567                        }
568                // - <thead> sous la forme |{{titre}}|{{titre}}|
569                //   Attention thead oblige a avoir tbody
570                        else if (preg_match($reg_line1, $ligne)) {
571                                preg_match_all('/\|([^|]*)/S', $ligne, $cols);
572                                $ligne='';$cols= $cols[1];
573                                $colspan=1;
574                                for($c=count($cols)-1; $c>=0; $c--) {
575                                        $attr='';
576                                        if($cols[$c]=='<') {
577                                          $colspan++;
578                                        } else {
579                                          if($colspan>1) {
580                                                $attr= " colspan='$colspan'";
581                                                $colspan=1;
582                                          }
583                                          // inutile de garder le strong qui n'a servi que de marqueur
584                                          $cols[$c] = str_replace(array('{','}'), '', $cols[$c]);
585                                          $ligne= "<th scope='col'$attr>$cols[$c]</th>$ligne";
586                                        }
587                                }
588
589                                $debut_table .= "<thead><tr class='row_first'>".
590                                        $ligne."</tr></thead>\n";
591                                $l = 0;
592                        }
593                }
594
595                // Sinon ligne normale
596                if ($l) {
597                        // Gerer les listes a puce dans les cellules
598                        if (strpos($ligne,"\n-*")!==false OR strpos($ligne,"\n-#")!==false)
599                                $ligne = traiter_listes($ligne);
600
601                        // tout mettre dans un tableau 2d
602                        preg_match_all('/\|([^|]*)/S', $ligne, $cols);
603
604                        // Pas de paragraphes dans les cellules
605                        foreach ($cols[1] as &$col) {
606                                if (strlen($col = trim($col))) {
607                                        $col = preg_replace("/\n{2,}/S", "<br /> <br />", $col);
608                                        $col = str_replace("\n", _AUTOBR."\n", $col);
609                                }
610                        }
611
612                        // assembler le tableau
613                        $lignes[]= $cols[1];
614                }
615        }
616
617        // maintenant qu'on a toutes les cellules
618        // on prepare une liste de rowspan par defaut, a partir
619        // du nombre de colonnes dans la premiere ligne.
620        // Reperer egalement les colonnes numeriques pour les cadrer a droite
621        $rowspans = $numeric = array();
622        $n = count($lignes[0]);
623        $k = count($lignes);
624        for($i=0;$i<$n;$i++) {
625          $align = true;
626          for ($j=0;$j<$k;$j++) $rowspans[$j][$i] = 1;
627          for ($j=0;$j<$k;$j++) {
628            $cell = trim($lignes[$j][$i]);
629            if (preg_match($reg_line_all, $cell)) {
630                if (!preg_match('/^\d+([.,]?)\d*$/', $cell, $r))
631                  { $align = ''; break;}
632                else if ($r[1]) $align = $r[1];
633              }
634          }
635          $numeric[$i] = !$align ? '' :
636            (" style='text-align: " .
637             // http://www.w3.org/TR/REC-CSS2/tables.html#column-alignment
638             // specifie text-align: "," pour cadrer le long de la virgule
639             // mais les navigateurs ne l'implementent pas ou mal
640             (/* $align !== true ?"\"$align\"" : */ 'right') .
641             "'");
642        }
643
644        // et on parcourt le tableau a l'envers pour ramasser les
645        // colspan et rowspan en passant
646        $html = '';
647
648        for($l=count($lignes)-1; $l>=0; $l--) {
649                $cols= $lignes[$l];
650                $colspan=1;
651                $ligne='';
652
653                for($c=count($cols)-1; $c>=0; $c--) {
654                        $attr= $numeric[$c];
655                        $cell = trim($cols[$c]);
656                        if($cell=='<') {
657                          $colspan++;
658
659                        } elseif($cell=='^') {
660                          $rowspans[$l-1][$c]+=$rowspans[$l][$c];
661
662                        } else {
663                          if($colspan>1) {
664                                $attr .= " colspan='$colspan'";
665                                $colspan=1;
666                          }
667                          if(($x=$rowspans[$l][$c])>1) {
668                                $attr.= " rowspan='$x'";
669                          }
670                          $ligne= "\n<td".$attr.'>'.$cols[$c].'</td>'.$ligne;
671                        }
672                }
673
674                // ligne complete
675                $class = alterner($l+1, 'even', 'odd');
676                $html = "<tr class='row_$class'>$ligne</tr>\n$html";
677        }
678
679        return "\n\n<table".$GLOBALS['class_spip_plus'].$summary.">\n"
680                . $debut_table
681                . "<tbody>\n"
682                . $html
683                . "</tbody>\n"
684                . "</table>\n\n";
685}
686
687
688//
689// Traitement des listes (merci a Michael Parienti)
690//
691// http://doc.spip.org/@traiter_listes
692function traiter_listes ($texte) {
693        global $class_spip, $class_spip_plus;
694        $parags = preg_split(",\n[[:space:]]*\n,S", $texte);
695        $texte ='';
696
697        // chaque paragraphe est traite a part
698        while (list(,$para) = each($parags)) {
699                $niveau = 0;
700                $pile_li = $pile_type = array();
701                $lignes = explode("\n-", "\n" . $para);
702
703                // ne pas toucher a la premiere ligne
704                list(,$debut) = each($lignes);
705                $texte .= $debut;
706
707                // chaque item a sa profondeur = nb d'etoiles
708                $type ='';
709                while (list(,$item) = each($lignes)) {
710                        preg_match(",^([*]*|[#]*)([^*#].*)$,sS", $item, $regs);
711                        $profond = strlen($regs[1]);
712
713                        if ($profond > 0) {
714                                $ajout='';
715
716                                // changement de type de liste au meme niveau : il faut
717                                // descendre un niveau plus bas, fermer ce niveau, et
718                                // remonter
719                                $nouv_type = (substr($item,0,1) == '*') ? 'ul' : 'ol';
720                                $change_type = ($type AND ($type <> $nouv_type) AND ($profond == $niveau)) ? 1 : 0;
721                                $type = $nouv_type;
722
723                                // d'abord traiter les descentes
724                                while ($niveau > $profond - $change_type) {
725                                        $ajout .= $pile_li[$niveau];
726                                        $ajout .= $pile_type[$niveau];
727                                        if (!$change_type)
728                                                unset ($pile_li[$niveau]);
729                                        $niveau --;
730                                }
731
732                                // puis les identites (y compris en fin de descente)
733                                if ($niveau == $profond && !$change_type) {
734                                        $ajout .= $pile_li[$niveau];
735                                }
736
737                                // puis les montees (y compris apres une descente un cran trop bas)
738                                while ($niveau < $profond) {
739                                        if ($niveau == 0) $ajout .= "\n\n";
740                                        elseif (!isset($pile_li[$niveau])) {
741                                                $ajout .= "<li$class_spip>";
742                                                $pile_li[$niveau] = "</li>";
743                                        }
744                                        $niveau ++;
745                                        $ajout .= "<$type$class_spip_plus>";
746                                        $pile_type[$niveau] = "</$type>";
747                                }
748
749                                $ajout .= "<li$class_spip>";
750                                $pile_li[$profond] = "</li>";
751                        }
752                        else {
753                                $ajout = "\n-"; // puce normale ou <hr>
754                        }
755
756                        $texte .= $ajout . $regs[2];
757                }
758
759                // retour sur terre
760                $ajout = '';
761                while ($niveau > 0) {
762                        $ajout .= $pile_li[$niveau];
763                        $ajout .= $pile_type[$niveau];
764                        $niveau --;
765                }
766                $texte .= $ajout;
767
768                // paragraphe
769                $texte .= "\n\n";
770        }
771
772        // sucrer les deux derniers \n
773        return substr($texte, 0, -2);
774}
775
776
777// fonction en cas de texte extrait d'un serveur distant:
778// on ne sait pas (encore) rapatrier les documents joints
779// Sert aussi a nettoyer un texte qu'on veut mettre dans un <a> etc.
780// TODO: gerer les modeles ?
781// http://doc.spip.org/@supprime_img
782function supprime_img($letexte, $message=NULL) {
783        if ($message===NULL) $message = '(' . _T('img_indisponible') . ')';
784        return preg_replace(',<(img|doc|emb)([0-9]+)(\|([^>]*))?'.'\s*/?'.'>,i',
785                $message, $letexte);
786}
787
788//
789// Une fonction pour fermer les paragraphes ; on essaie de preserver
790// des paragraphes indiques a la main dans le texte
791// (par ex: on ne modifie pas un <p align='center'>)
792//
793// deuxieme argument : forcer les <p> meme pour un seul paragraphe
794//
795// http://doc.spip.org/@paragrapher
796function paragrapher($letexte, $forcer=true) {
797        global $class_spip;
798
799        $letexte = trim($letexte);
800        if (!strlen($letexte))
801                return '';
802
803        if ($forcer OR (
804        strstr($letexte,'<') AND preg_match(',<p\b,iS',$letexte)
805        )) {
806
807                // Ajouter un espace aux <p> et un "STOP P"
808                // transformer aussi les </p> existants en <p>, nettoyes ensuite
809                $letexte = preg_replace(',</?p\b\s?(.*?)>,iS', '<STOP P><p \1>',
810                        '<p>'.$letexte.'<STOP P>');
811
812                // Fermer les paragraphes (y compris sur "STOP P")
813                $letexte = preg_replace(',(<p\s.*)(</?(STOP P|'._BALISES_BLOCS.')[>[:space:]]),UimsS',
814                        "\n\\1</p>\n\\2", $letexte);
815
816                // Supprimer les marqueurs "STOP P"
817                $letexte = str_replace('<STOP P>', '', $letexte);
818
819                // Reduire les blancs dans les <p>
820                $u = @$GLOBALS['meta']['pcre_u'];
821                $letexte = preg_replace(',(<p\b.*>)\s*,UiS'.$u, '\1',$letexte);
822                $letexte = preg_replace(',\s*(</p\b.*>),UiS'.$u, '\1',$letexte);
823
824                // Supprimer les <p xx></p> vides
825                $letexte = preg_replace(',<p\b[^<>]*></p>\s*,iS'.$u, '',
826                        $letexte);
827
828                // Renommer les paragraphes normaux
829                $letexte = str_replace('<p >', "<p$class_spip>",
830                        $letexte);
831
832        }
833
834        return $letexte;
835}
836
837// http://doc.spip.org/@traiter_poesie
838function traiter_poesie($letexte)
839{
840        if (preg_match_all(",<(poesie|poetry)>(.*)<\/(poesie|poetry)>,UimsS",
841        $letexte, $regs, PREG_SET_ORDER)) {
842                $u = "/\n[\s]*\n/S" . $GLOBALS['meta']['pcre_u'];
843                foreach ($regs as $reg) {
844                        $lecode = preg_replace(",\r\n?,S", "\n", $reg[2]);
845                        $lecode = preg_replace($u, "\n&nbsp;\n",$lecode);
846                        $lecode = "<blockquote class=\"spip_poesie\">\n<div>"
847                                .preg_replace("/\n+/", "</div>\n<div>", trim($lecode))
848                                ."</div>\n</blockquote>\n\n";
849                        $letexte = str_replace($reg[0], $lecode, $letexte);
850                }
851        }
852        return $letexte;
853}
854
855// Harmonise les retours chariots et mange les paragraphes html
856// http://doc.spip.org/@traiter_retours_chariots
857function traiter_retours_chariots($letexte) {
858        $letexte = preg_replace(",\r\n?,S", "\n", $letexte);
859        $letexte = preg_replace(",<p[>[:space:]],iS", "\n\n$0", $letexte);
860        $letexte = preg_replace(",</p[>[:space:]],iS", "$0\n\n", $letexte);
861        return $letexte;
862}
863
864// Ces deux constantes permettent de proteger certains caracteres
865// en les remplacanat par des caracteres "illegaux". (cf corriger_caracteres)
866
867define('_RACCOURCI_PROTEGER', "{}_-");
868define('_RACCOURCI_PROTECTEUR', "\x1\x2\x3\x4");
869
870define('_RACCOURCI_BALISE', ",</?[a-z!][^<>]*[".preg_quote(_RACCOURCI_PROTEGER)."][^<>]*>,imsS");
871
872// Nettoie un texte, traite les raccourcis autre qu'URL, la typo, etc.
873
874// mais d'abord, une callback de reconfiguration des raccourcis
875// a partir de globales (est-ce old-style ? on conserve quand meme
876// par souci de compat ascendante)
877function personnaliser_raccourcis(&$ruleset){
878        if (isset($GLOBALS['debut_intertitre']) AND $rule=$ruleset->getRule('intertitres')){
879                $rule->replace[0] = preg_replace(',<[^>]*>,Uims',$GLOBALS['debut_intertitre'],$rule->replace[0]);
880                $rule->replace[1] = preg_replace(',<[^>]*>,Uims',$GLOBALS['fin_intertitre'],$rule->replace[1]);
881                $ruleset->addRules(array('intertitres'=>$rule));
882        }
883        if (isset($GLOBALS['debut_gras']) AND $rule=$ruleset->getRule('gras')){
884                $rule->replace[0] = preg_replace(',<[^>]*>,Uims',$GLOBALS['debut_gras'],$rule->replace[0]);
885                $rule->replace[1] = preg_replace(',<[^>]*>,Uims',$GLOBALS['fin_gras'],$rule->replace[1]);
886                $ruleset->addRules(array('gras'=>$rule));
887        }
888        if (isset($GLOBALS['debut_italique']) AND $rule=$ruleset->getRule('italiques')){
889                $rule->replace[0] = preg_replace(',<[^>]*>,Uims',$GLOBALS['debut_italique'],$rule->replace[0]);
890                $rule->replace[1] = preg_replace(',<[^>]*>,Uims',$GLOBALS['fin_italique'],$rule->replace[1]);
891                $ruleset->addRules(array('italiques'=>$rule));
892        }
893        if (isset($GLOBALS['ligne_horizontale']) AND $rule=$ruleset->getRule('ligne-horizontale')){
894                $rule->replace = preg_replace(',<[^>]*>,Uims',$GLOBALS['ligne_horizontale'],$rule->replace);
895                $ruleset->addRules(array('ligne-horizontale'=>$rule));
896        }
897        if (isset($GLOBALS['toujours_paragrapher']) AND !$GLOBALS['toujours_paragrapher']
898          AND $rule=$ruleset->getRule('toujours-paragrapher')) {
899                $rule->disabled = true;
900                $ruleset->addRules(array('toujours-paragrapher'=>$rule));
901        }
902}
903
904// http://doc.spip.org/@traiter_raccourcis
905function traiter_raccourcis($t) {
906        static $wheel;
907        // Appeler les fonctions de pre_traitement
908        $t = pipeline('pre_propre', $t);
909
910        if (!isset($wheel)) {
911                $ruleset = SPIPTextWheelRuleset::loader(
912                        $GLOBALS['spip_wheels']['raccourcis'],'personnaliser_raccourcis'
913                );
914                $wheel = new TextWheel($ruleset);
915
916                if (_request('var_mode') == 'wheel'
917                AND autoriser('debug')) {
918                        $f = $wheel->compile();
919                        echo "<pre>\n".htmlspecialchars($f)."</pre>\n";
920                        exit;
921                }
922        }
923
924        // Gerer les notes (ne passe pas dans le pipeline)
925        $notes = charger_fonction('notes', 'inc');
926        list($t, $mes_notes) = $notes($t);
927
928        $t = $wheel->text($t);
929
930        // Appeler les fonctions de post-traitement
931        $t = pipeline('post_propre', $t);
932
933        if ($mes_notes)
934                $notes($mes_notes);
935
936        // hack: wrap des autobr dans l'espace prive, pour affichage css
937        // car en css on ne sait pas styler l'element BR
938        if (_DIR_RACINE) {
939                $manual = "<span style='color:green;'>&#x21B5;";
940                $auto = "<span style='color:orange;'>&para;";
941                foreach (array('t', 'mes_notes') as $k) {
942                        if (false !== strpos(strtolower($$k), '<br')) {
943                                $$k = preg_replace("/<br\b.*>/UiS", "$manual\\0</span>", $$k);
944                                if (_AUTOBR)
945                                        $$k = str_replace($manual._AUTOBR, $auto._AUTOBR, $$k);
946                        }
947                }
948        }
949
950        return $t;
951}
952
953
954// Filtre a appliquer aux champs du type #TEXTE*
955// http://doc.spip.org/@propre
956function propre($t, $connect=null) {
957        // les appels directs a cette fonction depuis le php de l'espace
958        // prive etant historiquement ecrits sans argment $connect
959        // on utilise la presence de celui-ci pour distinguer les cas
960        // ou il faut passer interdire_script explicitement
961        // les appels dans les squelettes (de l'espace prive) fournissant un $connect
962        // ne seront pas perturbes
963        $interdire_script = false;
964        if (is_null($connect)){
965                $connect = '';
966                $interdire_script = true;
967        }
968
969        if (!$t) return strval($t);
970
971        $t = echappe_html($t);
972        $t = expanser_liens($t,$connect);
973        $t = traiter_raccourcis($t);
974        $t = echappe_retour_modeles($t, $interdire_script);
975
976        return $t;
977}
978?>
Note: See TracBrowser for help on using the repository browser.