Newer
Older
/***************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2008 *
* Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
* *
* Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
* Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
\***************************************************************************/
if (!defined("_ECRIRE_INC_VERSION")) return;
//
// Gerer les variables de personnalisation, qui peuvent provenir
// des fichiers d'appel, en verifiant qu'elles n'ont pas ete passees
// par le visiteur (sinon, pas de cache)
//
// http://doc.spip.org/@tester_variable
function tester_variable($var, $val){
if (!isset($GLOBALS[$var]))
return $GLOBALS[$var] = $val;
if (
isset($_REQUEST[$var])
AND $GLOBALS[$var] == $_REQUEST[$var]
)
die ("tester_variable: $var interdite");
return $GLOBALS[$var];
// Init des globales reglant la typo de propre
// voir aussi traiter_raccourci_glossaire et traiter_raccourci_notes
// Retourne aussi un double tableau raccourci / texte-clair les utilisant.
global $class_spip, $class_spip_plus;
// class_spip : savoir si on veut class="spip" sur p i strong & li
// class_spip_plus : class="spip" sur les ul ol h3 hr quote table...
// la difference c'est que des css specifiques existent pour les seconds
tester_variable('class_spip', ''); /*' class="spip"'*/
tester_variable('class_spip_plus', ' class="spip"');
tester_variable('toujours_paragrapher', true);
return array(array(
/* 0 */ "/\n(----+|____+)/S",
/* 1 */ "/\n-- */S",
/* 2 */ "/\n- */S", /* DOIT rester a cette position */
/* 3 */ "/\n_ +/S",
/* 4 */ "/(^|[^{])[{][{][{]/S",
/* 5 */ "/[}][}][}]($|[^}])/S",
/* 6 */ "/(( *)\n){2,}(<br\s*\/?".">)?/S",
/* 7 */ "/[{][{]/S",
/* 8 */ "/[}][}]/S",
/* 9 */ "/[{]/S",
/* 10 */ "/[}]/S",
/* 11 */ "/(?:<br\s*\/?".">){2,}/S",
/* 12 */ "/<p>\n*(?:<br\s*\/?".">\n*)*/S",
/* 13 */ "/<quote>/S",
/* 14 */ "/<\/quote>/S",
/* 15 */ "/<\/?intro>/S"
),
array(
/* 0 */ "\n\n" . tester_variable('ligne_horizontale', "\n<hr$class_spip_plus />\n") . "\n\n",
/* 1 */ "\n<br />— ",
/* 3 */ "\n<br />",
/* 4 */ "\$1\n\n" . tester_variable('debut_intertitre', "\n<h3$class_spip_plus>"),
/* 5 */ tester_variable('fin_intertitre', "</h3>\n") ."\n\n\$1",
/* 6 */ "<p>",
/* 7 */ tester_variable('debut_gras', "<strong$class_spip>"),
/* 8 */ tester_variable('fin_gras', '</strong>'),
/* 9 */ tester_variable('debut_italique', "<i$class_spip>"),
/* 10 */ tester_variable('fin_italique', '</i>'),
/* 11 */ "<p>",
/* 12 */ "<p>",
/* 13 */ "<blockquote$class_spip_plus><p>",
/* 14 */ "</blockquote><p>",
/* 15 */ ""
)
);
}
// On initialise la puce pour eviter find_in_path() a chaque rencontre de \n-
// Mais attention elle depend de la direction et de X_fonctions.php, ainsi que
// de l'espace choisi (public/prive)
// http://doc.spip.org/@definir_puce
function definir_puce() {
// Attention au sens, qui n'est pas defini de la meme facon dans
// l'espace prive (spip_lang est la langue de l'interface, lang_dir
// celle du texte) et public (spip_lang est la langue du texte)
$dir = _DIR_RESTREINT ? lang_dir() : lang_dir($GLOBALS['spip_lang']);
$p = 'puce' . (test_espace_prive() ? '_prive' : '');
if ($dir == 'rtl') $p .= '_rtl';
tester_variable($p, 'AUTO');
if ($GLOBALS[$p] == 'AUTO') {
$img = find_in_path($p.'.gif');
list(,,,$size) = @getimagesize($img);
$GLOBALS[$p] = '<img src="'.$img.'" '
.$size.' alt="-" />';
}
return $GLOBALS[$p];
// Diverses fonctions essentielles
// XHTML - Preserver les balises-bloc : on liste ici tous les elements
// dont on souhaite qu'ils provoquent un saut de paragraphe
define('_BALISES_BLOCS',
.'t(able|[rdh]|body|foot|extarea)|'
.'form|object|center|marquee|address|'
.'d[ltd]|script|noscript|map|button|fieldset');
// Ne pas afficher le chapo si article virtuel
// http://doc.spip.org/@nettoyer_chapo
return (substr($chapo,0,1) == "=") ? '' : $chapo;
//
// Echapper les les elements perilleux en les passant en base64
//
// Creer un bloc base64 correspondant a $rempl ; au besoin en marquant
// une $source differente ; le script detecte automagiquement si ce qu'on
// echappe est un div ou un span
// http://doc.spip.org/@code_echappement
function code_echappement($rempl, $source='', $no_transform=false) {
if (!strlen($rempl)) return '';
// Tester si on echappe en span ou en div
$mode = preg_match(',</?('._BALISES_BLOCS.')[>[:space:]],iS', $rempl) ?
'div' : 'span';
$return = '';
// Decouper en morceaux, base64 a des probleme selon la taille de la pile
$taille = 30000;
for($i = 0; $i < strlen($rempl); $i += $taille) {
// Convertir en base64
$base64 = base64_encode(substr($rempl, $i, $taille));
$return .= inserer_attribut("<$mode class=\"base64$source\">",
'title', $base64) ."</$mode>";
}
Fil
a validé
return $return
. ((!$no_transform AND $mode == 'div')
? "\n\n"
: ''
);
;
Fil
a validé
// Echapper les <html>...</ html>
Christian Lefebvre
a validé
// http://doc.spip.org/@traiter_echap_html_dist
Fil
a validé
function traiter_echap_html_dist($regs) {
return $regs[3];
}
// Echapper les <code>...</ code>
Christian Lefebvre
a validé
// http://doc.spip.org/@traiter_echap_code_dist
Fil
a validé
function traiter_echap_code_dist($regs) {
$echap = htmlspecialchars($regs[3]); // il ne faut pas passer dans entites_html, ne pas transformer les &#xxx; du code !
Fil
a validé
// ne pas mettre le <div...> s'il n'y a qu'une ligne
if (is_int(strpos($echap,"\n"))) {
// supprimer les sauts de ligne debut/fin
// (mais pas les espaces => ascii art).
$echap = preg_replace("/^[\n\r]+|[\n\r]+$/s", "", $echap);
$echap = nl2br($echap);
$echap = "<div style='text-align: left;' "
Fil
a validé
. "class='spip_code' dir='ltr'><code>"
.$echap."</code></div>";
} else {
Fil
a validé
$echap = "<code class='spip_code' "
."dir='ltr'>".$echap."</code>";
Fil
a validé
$echap = str_replace("\t",
" ", $echap);
$echap = str_replace(" ", " ", $echap);
return $echap;
}
// Echapper les <cadre>...</ cadre> aka <frame>...</ frame>
Christian Lefebvre
a validé
// http://doc.spip.org/@traiter_echap_cadre_dist
Fil
a validé
function traiter_echap_cadre_dist($regs) {
$echap = trim(entites_html($regs[3]));
$n = substr_count($echap, "\n") + 1;
$echap = "\n<textarea readonly='readonly' cols='40' rows='$n' class='spip_cadre' dir='ltr'>$echap</textarea>";
return generer_form_ecrire('', $echap, " method='get'");
Fil
a validé
}
Christian Lefebvre
a validé
// http://doc.spip.org/@traiter_echap_frame_dist
Fil
a validé
function traiter_echap_frame_dist($regs) {
return traiter_echap_cadre_dist($regs);
}
Christian Lefebvre
a validé
// http://doc.spip.org/@traiter_echap_script_dist
Fil
a validé
function traiter_echap_script_dist($regs) {
// rendre joli (et inactif) si c'est un script language=php
if (preg_match(',<script\b[^>]+php,ims',
$regs[0]))
return highlight_string($regs[0],true);
// Cas normal : le script passe tel quel
Fil
a validé
return $regs[0];
}
// - pour $source voir commentaire infra (echappe_retour)
// - pour $no_transform voir le filtre post_autobr dans inc_filtres.php3
// http://doc.spip.org/@echappe_html
function echappe_html($letexte, $source='', $no_transform=false,
if (!is_string($letexte) or !strlen($letexte))
return $letexte;
if (!$preg) $preg = ',<(html|code|cadre|frame|script)'
.'(\s[^>]*)?'
.'>(.*)</\1>,UimsS';
$letexte, $matches, PREG_SET_ORDER))
foreach ($matches as $regs) {
// echappements tels quels ?
if ($no_transform) {
$echap = $regs[0];
// sinon les traiter selon le cas
Fil
a validé
else if (function_exists($f = 'traiter_echap_'.strtolower($regs[1])))
$echap = $f($regs);
else if (function_exists($f = $f.'_dist'))
$echap = $f($regs);
$letexte = str_replace($regs[0],
code_echappement($echap, $source, $no_transform),
if ($no_transform)
return $letexte;
// Gestion du TeX
if (strpos($letexte, "<math>") !== false) {
$letexte = traiter_math($letexte, $source);
// Echapper le php pour faire joli (ici, c'est pas pour la securite)
if (preg_match_all(
',<[?].*($|[?]>),UisS',
$letexte, $matches, PREG_SET_ORDER))
foreach ($matches as $regs) {
$letexte = str_replace($regs[0],
code_echappement(highlight_string($regs[0],true), $source),
$letexte);
}
// Traitement final des echappements
// Rq: $source sert a faire des echappements "a soi" qui ne sont pas nettoyes
// par propre() : exemple dans ecrire/inc_articles_ortho.php, $source='ORTHO'
// ou encore dans typo()
// http://doc.spip.org/@echappe_retour
function echappe_retour($letexte, $source='', $filtre = "") {
Fil
a validé
if (strpos($letexte,"base64$source")) {
# spip_log(htmlspecialchars($letexte)); ## pour les curieux
if (preg_match_all(
',<(span|div) class=[\'"]base64'.$source.'[\'"]\s.*>\s*</\1>,UmsS',
$letexte, $regs, PREG_SET_ORDER)) {
foreach ($regs as $reg) {
Fil
a validé
$rempl = base64_decode(extraire_attribut($reg[0], 'title'));
if ($filtre) $rempl = $filtre($rempl);
$letexte = str_replace($reg[0], $rempl, $letexte);
}
}
}
return $letexte;
esj
a validé
}
// Reinserer le javascript de confiance (venant des modeles)
function echappe_retour_modeles($letexte)
{
$letexte = echappe_retour($letexte);
// Dans l'espace prive, securiser ici
if (!_DIR_RESTREINT)
$letexte = interdire_scripts($letexte);
return trim($letexte);
}
// http://doc.spip.org/@nettoyer_raccourcis_typo
function nettoyer_raccourcis_typo($texte, $connect=''){
$texte = pipeline('nettoyer_raccourcis_typo',$texte);
// remplacer les liens
if (preg_match_all(',[[]([^][]*)->(>?)([^][]*)[]],S', $texte, $regs, PREG_SET_ORDER))
foreach ($regs as $reg) {
list ($titre,,)= traiter_raccourci_lien_atts($reg[1]);
$titre = calculer_url($reg[3], $titre, 'titre', $connect);
$titre = corriger_typo(supprimer_tags($titre));
$texte = str_replace($reg[0], $titre, $texte);
}
// supprimer les notes
$texte = preg_replace(",[[][[]([^]]|[]][^]])*[]][]],UimsS","",$texte);
// supprimer les codes typos
$texte = str_replace(array('}','{'), '', $texte);
// supprimer les tableaux
$texte = preg_replace(",(^|\r)\|.*\|\r,s", "\r", $texte);
return $texte;
}
// http://doc.spip.org/@couper
function couper($texte, $taille=50, $suite = ' (...)') {
if (!strlen($texte) OR $taille <= 0) return '';
$offset = 400 + 2*$taille;
if ( $offset<strlen($texte)
&& ($p_tag_ouvrant = strpos($texte,'<',$offset))!==NULL){
$p_tag_fermant = strpos($texte,'>',$offset);
if ($p_tag_fermant<$p_tag_ouvrant)
$offset += $p_tag_fermant; // prolonger la coupe jusqu'au tag fermant suivant eventuel
}
$texte = substr($texte, 0, $offset); /* eviter de travailler sur 10ko pour extraire 150 caracteres */
// on utilise les \r pour passer entre les gouttes
Fil
a validé
$texte = str_replace("\r\n", "\n", $texte);
$texte = str_replace("\r", "\n", $texte);
// sauts de ligne et paragraphes
$texte = preg_replace("/<(p|br)( [^>]*)?".">/", "\r", $texte);
Fil
a validé
// supprimer les traits, lignes etc
$texte = preg_replace("/(^|\r|\n)(-[-#\*]*|_ )/", "\r", $texte);
Fil
a validé
// supprimer les tags
$texte = supprimer_tags($texte);
$texte = trim(str_replace("\n"," ", $texte));
$texte .= "\n"; // marquer la fin
// travailler en accents charset
$texte = unicode2charset(html2unicode($texte, /* secure */ true));
$texte = nettoyer_raccourcis_typo($texte);
// corriger la longueur de coupe
// en fonction de la presence de caracteres utf
if ($GLOBALS['meta']['charset']=='utf-8'){
$long = charset2unicode($texte);
$long = spip_substr($long, 0, max($taille,1));
$nbcharutf = preg_match_all("/(&#[0-9]{3,5};)/S",$long,$matches);
$taille += $nbcharutf;
}
// couper au mot precedent
$court = preg_replace("/([^\s][\s]+)[^\s]*\n?$/", "\\1", $long);
$points = $suite;
// trop court ? ne pas faire de (...)
$points = '';
$texte = preg_replace("/([^\s][\s]+)[^\s]*\n?$/", "\\1", $long);
// encore trop court ? couper au caractere
$texte = $long;
} else
$texte = $court;
if (strpos($texte, "\n")) // la fin est encore la : c'est qu'on n'a pas de texte de suite
$points = '';
// remettre les paragraphes
$texte = preg_replace("/\r+/", "\n\n", $texte);
$texte = preg_replace('/&#?[a-z0-9]*$/S', '', $texte);
// Les elements de propre()
// Securite : empecher l'execution de code PHP ou javascript ou autre malice
// http://doc.spip.org/@interdire_scripts
function interdire_scripts($source) {
$source = preg_replace(",<(\%|\?|/?[[:space:]]*(script|base)),imsS", "<\\1", $source);
return $source;
*/
// afficher joliment les <script>
Christian Lefebvre
a validé
// http://doc.spip.org/@echappe_js
function echappe_js($t,$class='') {
if (preg_match_all(',<script.*?($|</script.),isS', $t, $r, PREG_SET_ORDER))
foreach ($r as $regs)
$t = str_replace($regs[0],
"<code$class>".nl2br(htmlspecialchars($regs[0])).'</code>',
$t);
return $t;
}
function protege_js_modeles($t) {
if (isset($GLOBALS['visiteur_session'])){
if (preg_match_all(',<script.*?($|</script.),isS', $t, $r, PREG_SET_ORDER)){
if (!defined('_PROTEGE_JS_MODELES')){
include_spip('inc/acces');
define('_PROTEGE_JS_MODELES',creer_uniqid());
}
foreach ($r as $regs)
$t = str_replace($regs[0],code_echappement($regs[0],'javascript'._PROTEGE_JS_MODELES),$t);
}
if (preg_match_all(',<\?php.*?($|\?'.'>),isS', $t, $r, PREG_SET_ORDER)){
if (!defined('_PROTEGE_PHP_MODELES')){
include_spip('inc/acces');
define('_PROTEGE_PHP_MODELES',creer_uniqid());
}
foreach ($r as $regs)
$t = str_replace($regs[0],code_echappement($regs[0],'php'._PROTEGE_PHP_MODELES),$t);
}
}
return $t;
}
// Securite : empecher l'execution de code PHP, en le transformant en joli code
// l'espace prive est securise globalement par un appel explicite un interdire_script
// on desactive l'appel des squelettes tant que la protection globale est la
// a terme (tout l'espace prive en skel) il faudra mettre $protege_espace_prive = true
// http://doc.spip.org/@interdire_scripts
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
function interdire_scripts($t, $protege_espace_prive = false) {
if (_DIR_RESTREINT || $protege_espace_prive) {
// rien ?
if (!$t OR !strstr($t, '<')) return $t;
// echapper les tags asp/php
$t = str_replace('<'.'%', '<%', $t);
// echapper le php
$t = str_replace('<'.'?', '<?', $t);
// echapper le < script language=php >
$t = preg_replace(',<(script\b[^>]+\blanguage\b[^\w>]+php\b),UimsS', '<\1', $t);
// Pour le js, trois modes : parano (-1), prive (0), ok (1)
switch($GLOBALS['filtrer_javascript']) {
case 0:
if (!_DIR_RESTREINT)
$t = echappe_js($t,' style="color:red"');
break;
case -1:
$t = echappe_js($t);
break;
}
// pas de <base href /> svp !
$t = preg_replace(',<(base\s),iS', '<\1', $t);
// Reinserer les echappements des modeles
if (defined('_PROTEGE_JS_MODELES'))
$t = echappe_retour($t,"javascript"._PROTEGE_JS_MODELES);
if (defined('_PROTEGE_PHP_MODELES'))
$t = echappe_retour($t,"php"._PROTEGE_PHP_MODELES);
return $t;
}
// Securite : utiliser SafeHTML s'il est present dans ecrire/safehtml/
// http://doc.spip.org/@safehtml
function safehtml($t) {
static $safehtml;
# attention safehtml nettoie deux ou trois caracteres de plus. A voir
if (strpos($t,'<')===false)
return str_replace("\x00", '', $t);
$t = interdire_scripts($t); // jolifier le php
$t = echappe_js($t);
if (!isset($safehtml))
$safehtml = charger_fonction('safehtml', 'inc');
if ($safehtml)
$t = $safehtml($t);
return interdire_scripts($t); // interdire le php (2 precautions)
Antoine Pitrou
a validé
// Typographie generale
// avec protection prealable des balises HTML et SPIP
function typo($letexte, $echapper=true, $connect='') {
// Plus vite !
if (!$letexte) return $letexte;
if ($echapper)
$letexte = echappe_html($letexte, 'TYPO');
//
// Installer les modeles, notamment images et documents ;
//
// NOTE : propre() l'a deja fait
// sauf pour les textes renvoyes par calculer_url()
$letexte = traiter_modeles($mem = $letexte, false, $echapper ? 'TYPO' : '', $connect);
if ($letexte != $mem) $echapper = true;
unset($mem);
$letexte = corriger_typo($letexte);
// reintegrer les echappements
if ($echapper)
$letexte = echappe_retour($letexte, 'TYPO');
// Dans l'espace prive, securiser ici
// quand tout l'espace prive sera en skel, supprimer cette ligne et retablir
// $protege_espace_prive = true dans interdire_script
if (!_DIR_RESTREINT)
$letexte = interdire_scripts($letexte,true);
return $letexte;
}
// Correcteur typographique
function corriger_typo($letexte) {
// Plus vite !
if (!$letexte) return $letexte;
// Caracteres de controle "illegaux"
$letexte = corriger_caracteres($letexte);
// Charger & appliquer la fonction de typographie
if ($typographie = charger_fonction(lang_typo(), 'typographie')) {
// Proteger les caracteres typographiques a l'interieur des tags html
$protege = "!':;?~%-";
$illegal = "\x1\x2\x3\x4\x5\x6\x7\x8";
if (preg_match_all(",</?[a-z!][^<>]*[".preg_quote($protege)."][^<>]*>,imsS",
$letexte, $regs, PREG_SET_ORDER)) {
foreach ($regs as $reg) {
$insert = $reg[0];
// hack: on transforme les caracteres a proteger en les remplacant
// par des caracteres "illegaux". (cf corriger_caracteres())
$insert = strtr($insert, $protege, $illegal);
$letexte = str_replace($reg[0], $insert, $letexte);
}
$letexte = $typographie($letexte);
// Retablir les caracteres proteges
$letexte = strtr($letexte, $illegal, $protege);
}
# un message pour abs_url - on est passe en mode texte
$GLOBALS['mode_abs_url'] = 'texte';
// analyse des raccourcis issus de [TITRE->RACCOURCInnn] et connexes
define('_RACCOURCI_URL', ',^\s*(\w*?)\s*(\d+)(\?(.*?))?(#([^\s]*))?\s*$,S');
function typer_raccourci ($lien) {
if (!preg_match(_RACCOURCI_URL, $lien, $match)) return array();
$f = $match[1];
// valeur par defaut et alias historiques
if (!$f) $f = 'article';
else if ($f == 'art') $f = 'article';
else if ($f == 'br') $f = 'breve';
else if ($f == 'rub') $f = 'rubrique';
else if ($f == 'aut') $f = 'auteur';
else if ($f == 'doc' OR $f == 'im' OR $f == 'img' OR $f == 'image' OR $f == 'emb')
$f = 'document';
else if (preg_match(',^br..?ve$,S', $f)) $f = 'breve'; # accents :(
$match[0] = $f;
$match[2] = entites_html($match[2]);
return $match;
}
// Cherche un lien du type [->raccourci 123]
// associe a une fonction generer_url_raccourci()
//
// Valeur retournee selon le parametre $pour:
// 'tout' : tableau [U,C,T,L] (vise <a href="U" class='C' hreflang='L'>T</a>)
// 'titre': seulement T ci-dessus (i.e. le TITRE ci-dessus ou dans table SQL)
// 'url': seulement U (i.e. generer_url_RACCOURCI)
// http://doc.spip.org/@calculer_url
function calculer_url ($ref, $texte='', $pour='url', $connect='') {
include_spip('base/abstract_sql');
if ($match = typer_raccourci($ref)) {
@list($f,,$id,,$args,,$ancre) = $match;
$lien = charger_fonction('lien', 'inc', true);
if ($lien) {
$r = $lien($f,$id,$args,$ancre,$texte,$pour,$connect);
if ($r)
return ($pour=='tout') ? $r :
(($pour=='url') ? $r[0] :$r[2]);
spip_log("raccourci indefini $f");
if (preg_match(",^\s*(http:?/?/?|mailto:?)\s*$,iS", $ref))
return ($pour != 'tout') ? '' : array('','','','');
$lien = entites_html(trim($ref));
// Liens explicites
if (!$texte) {
$texte = str_replace('"', '', $lien);
if (strlen($texte)>40)
$texte = substr($texte,0,35).'...';
$texte = "<html>$texte</html>";
$class = "spip_url spip_out";
} else $class = "spip_out";
if ($pour == 'titre') return $texte;
// petites corrections d'URL
if (preg_match(",^www\.[^@]+$,S",$lien))
$lien = "http://".$lien;
else if (strpos($lien, "@") && email_valide($lien))
$lien = "mailto:".$lien;
if (preg_match(",^\s*mailto:,",$lien))
$class = "spip_mailto";
// class spip_ancre sur les ancres pures (internes a la page)
if (substr($lien,0,1) == '#')
$class = 'spip_ancre';
return ($pour == 'url') ? $lien : array($lien, $class, $texte, '');
// http://doc.spip.org/@traiter_tableau
function traiter_tableau($bloc) {
// Decouper le tableau en lignes
preg_match_all(',([|].*)[|]\n,UmsS', $bloc, $regs, PREG_PATTERN_ORDER);
$debut_table = $summary = '';
$l = 0;
$numeric = true;
// Traiter chaque ligne
foreach ($regs[1] as $ligne) {
$l ++;
// Gestion de la premiere ligne :
if ($l == 1) {
// - <caption> et summary dans la premiere ligne :
// || caption | summary || (|summary est optionnel)
if (preg_match(',^\|\|([^|]*)(\|(.*))?$,sS', $ligne, $cap)) {
if ($caption = trim($cap[1]))
$debut_table .= "<caption>".$caption."</caption>\n";
$summary = ' summary="'.entites_html(trim($cap[3])).'"';
}
// - <thead> sous la forme |{{titre}}|{{titre}}|
// Attention thead oblige a avoir tbody
else if (preg_match(',^(\|([[:space:]]*(:?{{[^}]+}}[[:space:]]*)?|<))+$,sS',
preg_match_all("/\|([^|]*)/S", $ligne, $cols);
$ligne='';$cols= $cols[1];
$colspan=1;
for($c=count($cols)-1; $c>=0; $c--) {
$attr='';
if($cols[$c]=='<') {
$colspan++;
} else {
if($colspan>1) {
$attr= " colspan='$colspan'";
$colspan=1;
}
$ligne= "<th scope='col'$attr>$cols[$c]</th>$ligne";
}
}
$debut_table .= "<thead><tr class='row_first'>".
$ligne."</tr></thead>\n";
$l = 0;
}
}
// Sinon ligne normale
if ($l) {
// Gerer les listes a puce dans les cellules
if (strpos($ligne,"\n-*")!==false OR strpos($ligne,"\n-#")!==false)
$ligne = traiter_listes($ligne);
$ligne = preg_replace("/\n{2,}/", "<br />\n", $ligne);
// tout mettre dans un tableau 2d
preg_match_all("/\|([^|]*)/S", $ligne, $cols);
$lignes[]= $cols[1];
}
}
// maintenant qu'on a toutes les cellules
// on prepare une liste de rowspan par defaut, a partir
// du nombre de colonnes dans la premiere ligne
$rowspans = array();
for ($i=0; $i<count($lignes[0]); $i++)
$rowspans[] = 1;
// et on parcourt le tableau a l'envers pour ramasser les
// colspan et rowspan en passant
for($l=count($lignes)-1; $l>=0; $l--) {
$cols= $lignes[$l];
$colspan=1;
$ligne='';
for($c=count($cols)-1; $c>=0; $c--) {
$attr='';
if($cols[$c]=='<') {
$colspan++;
} elseif($cols[$c]=='^') {
$rowspans[$c]++;
} else {
if($colspan>1) {
$attr.= " colspan='$colspan'";
$colspan=1;
}
if($rowspans[$c]>1) {
$attr.= " rowspan='$rowspans[$c]'";
$rowspans[$c]=1;
}
$ligne= "\n<td".$attr.'>'.$cols[$c].'</td>'.$ligne;
$numeric &= (preg_match('/[{<]/',$cols[$c][0]) || is_numeric($cols[$c]));
// ligne complete
$class = 'row_'.alterner($l+1, 'even', 'odd');
$html = "<tr class=\"$class\">" . $ligne . "</tr>\n".$html;
if ($numeric)
$html = str_replace("\n<td", "\n<td style='text-align: right'", $html);
return "\n\n<table".$GLOBALS['class_spip_plus'].$summary.">\n"
. "</table>\n\n";
// Traitement des listes (merci a Michael Parienti)
// http://doc.spip.org/@traiter_listes
function traiter_listes ($texte) {
global $class_spip, $class_spip_plus;
$parags = preg_split(",\n[[:space:]]*\n,S", $texte);
// chaque paragraphe est traite a part
while (list(,$para) = each($parags)) {
$niveau = 0;
$lignes = explode("\n-", "\n" . $para);
// ne pas toucher a la premiere ligne
list(,$debut) = each($lignes);
$texte .= $debut;
// chaque item a sa profondeur = nb d'etoiles
while (list(,$item) = each($lignes)) {
preg_match(",^([*]*|[#]*)([^*#].*)$,sS", $item, $regs);
$profond = strlen($regs[1]);
if ($profond > 0) {
// changement de type de liste au meme niveau : il faut
// descendre un niveau plus bas, fermer ce niveau, et
// remonter
$nouv_type = (substr($item,0,1) == '*') ? 'ul' : 'ol';
$change_type = ($type AND ($type <> $nouv_type) AND ($profond == $niveau)) ? 1 : 0;
$type = $nouv_type;
// d'abord traiter les descentes
while ($niveau > $profond - $change_type) {
$ajout .= $pile_li[$niveau];
$ajout .= $pile_type[$niveau];
// puis les identites (y compris en fin de descente)
if ($niveau == $profond && !$change_type) {
}
// puis les montees (y compris apres une descente un cran trop bas)
while ($niveau < $profond) {
if ($niveau == 0) $ajout .= "\n\n";
$ajout .= "<$type$class_spip_plus>";
$pile_type[$niveau] = "</$type>";
$pile_li[$profond] = "</li>";
Fil
a validé
$ajout = "\n-"; // puce normale ou <hr>
Fil
a validé
$texte .= $ajout . $regs[2];
}
// retour sur terre
while ($niveau > 0) {
$ajout .= $pile_li[$niveau];
$ajout .= $pile_type[$niveau];
$niveau --;
}
$texte .= $ajout;
// paragraphe
$texte .= "\n\n";
}
// sucrer les deux derniers \n
return substr($texte, 0, -2);
}
// fonction en cas de texte extrait d'un serveur distant:
// on ne sait pas (encore) rapatrier les documents joints
esj
a validé
// Sert aussi a nettoyer un texte qu'on veut mettre dans un <a> etc.
// TODO: gerer les modeles ?
// http://doc.spip.org/@supprime_img
esj
a validé
function supprime_img($letexte, $message=NULL) {
if ($message===NULL) $message = '(' . _T('img_indisponible') . ')';
return preg_replace(',<(img|doc|emb)([0-9]+)(\|([^>]*))?'.'\s*/?'.'>,i',
esj
a validé
$message, $letexte);
// traite les modeles (dans la fonction typo), en remplacant
// le raccourci <modeleN|parametres> par la page calculee a
// partir du squelette modeles/modele.html
// Le nom du modele doit faire au moins trois caracteres (evite <h2>)
// Si $doublons==true, on repere les documents sans calculer les modeles
// mais on renvoie les params (pour l'indexation par le moteur de recherche)
// http://doc.spip.org/@traiter_modeles
define('_RACCOURCI_MODELE',
'(<([a-z_-]{3,})' # <modele
.'\s*([0-9]*)\s*' # id
.'([|](?:<[^<>]*>|[^>])*?)?' # |arguments (y compris des tags <...>)
.'\s*/?'.'>)' # fin du modele >
.'\s*(<\/a>)?' # eventuel </a>
);
define('_RACCOURCI_MODELE_DEBUT', '@^' . _RACCOURCI_MODELE .'@is');
function traiter_modeles($texte, $doublons=false, $echap='', $connect='') {
// detecter les modeles (rapide)
Fil
a validé
if (preg_match_all('/<[a-z_-]{3,}\s*[0-9|]+/iS',
$texte, $matches, PREG_SET_ORDER)) {
include_spip('public/assembler');
foreach ($matches as $match) {
// Recuperer l'appel complet (y compris un eventuel lien)
$a = strpos($texte,$match[0]);
preg_match(_RACCOURCI_MODELE_DEBUT, substr($texte, $a), $regs);
$regs[]=""; // s'assurer qu'il y a toujours un 5e arg, eventuellement vide
list(,$mod, $type, $id, $params, $fin) = $regs;
if ($fin AND preg_match(
',<a\s[^<>]*>\s*$,i', substr($texte, 0, $a), $r)) {
$lien = array(
extraire_attribut($r[0],'href'),
extraire_attribut($r[0],'class')
);
$n = strlen($r[0]);
$a -= $n;
$cherche = $n + strlen($regs[0]);
} else {
$lien = false;
$cherche = strlen($mod);
// calculer le modele
# hack articles_edit, breves_edit, indexation
if ($doublons)
$texte .= preg_replace(',[|][^|=]*,s',' ',$params);
$modele = inclure_modele($type, $id, $params, $lien, $connect);
// le remplacer dans le texte
if ($modele !== false) {
$modele = protege_js_modeles($modele);
$rempl = code_echappement($modele, $echap);
. substr($texte, $a+$cherche);
// hack pour tout l'espace prive
if (((!_DIR_RESTREINT) OR ($doublons)) AND ($id) AND (in_array($type,array('doc','emb','img'))))
$GLOBALS['doublons_documents_inclus'][] = $id;
}
}
return $texte;
}
//
// Une fonction pour fermer les paragraphes ; on essaie de preserver
// des paragraphes indiques a la main dans le texte
// (par ex: on ne modifie pas un <p align='center'>)
//
// deuxieme argument : forcer les <p> meme pour un seul paragraphe
//
// http://doc.spip.org/@paragrapher
function paragrapher($letexte, $forcer=true) {
$letexte = trim($letexte);
if (!strlen($letexte))
return '';
if ($forcer OR (
strstr($letexte,'<') AND preg_match(',<p\b,iS',$letexte)
)) {
// Ajouter un espace aux <p> et un "STOP P"
// transformer aussi les </p> existants en <p>, nettoyes ensuite
$letexte = preg_replace(',</?p(\s([^>]*))?'.'>,iS', '<STOP P><p \2>',
'<p>'.$letexte.'<STOP P>');
// Fermer les paragraphes (y compris sur "STOP P")
$letexte = preg_replace(
',(<p\s.*)(</?(STOP P|'._BALISES_BLOCS.')[>[:space:]]),UimsS',
"\n\\1</p>\n\\2", $letexte);
// Supprimer les marqueurs "STOP P"
$letexte = str_replace('<STOP P>', '', $letexte);
// Reduire les blancs dans les <p>
renato
a validé
// Do not delete multibyte utf character just before </p> having last byte equal to whitespace
$u = ($GLOBALS['meta']['charset']=='utf-8' && test_pcre_unicode()) ? 'u':'S';
$letexte = preg_replace(
renato
a validé
',(<p(>|\s[^>]*)>)\s*|\s*(</p[>[:space:]]),'.$u.'i', '\1\3',