Skip to content
Extraits de code Groupes Projets
texte.php 43,3 ko
Newer Older
<?php
Yannick Patois's avatar
Yannick Patois a validé

Fil's avatar
Fil a validé
/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
Fil's avatar
Fil a validé
 *  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;
Yannick Patois's avatar
Yannick Patois a validé

Fil's avatar
Fil a validé
include_spip('inc/filtres');
Fil's avatar
Fil a validé
include_spip('inc/lang');
Yannick Patois's avatar
Yannick Patois a validé

//
// 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]))

	if (
		isset($_REQUEST[$var])
		AND $GLOBALS[$var] == $_REQUEST[$var]
	)
		die ("tester_variable: $var interdite");
// 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.

Christian Lefebvre's avatar
Christian Lefebvre a validé
// http://doc.spip.org/@traiter_variables_sales
Fil's avatar
Fil a validé
function traiter_variables_sales() {
	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",
Fil's avatar
Fil a validé
		/* 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 />&mdash;&nbsp;",
Fil's avatar
Fil a validé
		/* 2 */ 	"\n<br />".definir_puce()."&nbsp;",
		/* 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-
Fil's avatar
Fil a validé
// 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']);
Fil's avatar
Fil a validé
	$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];
Yannick Patois's avatar
Yannick Patois a validé

Yannick Patois's avatar
Yannick Patois a validé

Fil's avatar
Fil a validé
// XHTML - Preserver les balises-bloc : on liste ici tous les elements
// dont on souhaite qu'ils provoquent un saut de paragraphe
Fil's avatar
Fil a validé
	'div|pre|ul|ol|li|blockquote|h[1-6r]|'
	.'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
function nettoyer_chapo($chapo){
	return (substr($chapo,0,1) == "=") ? '' : $chapo;
// Echapper les les elements perilleux en les passant en base64
Yannick Patois's avatar
Yannick Patois a validé

// 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) {
	// Tester si on echappe en span ou en div
	$mode = preg_match(',</?('._BALISES_BLOCS.')[>[:space:]],iS', $rempl) ?
	$return = '';

	// Decouper en morceaux, base64 a des probleme selon la taille de la pile
	$taille = 30000;
	for($i = 0; $i < strlen($rempl); $i += $taille) {
		$base64 = base64_encode(substr($rempl, $i, $taille));
		$return .= inserer_attribut("<$mode class=\"base64$source\">",
				'title', $base64) ."</$mode>";
	return $return
		. ((!$no_transform AND $mode == 'div')
			? "\n\n"
			: ''
		);
;
Yannick Patois's avatar
Yannick Patois a validé

// http://doc.spip.org/@traiter_echap_html_dist
function traiter_echap_html_dist($regs) {
	return $regs[3];
}

// Echapper les <code>...</ code>
// http://doc.spip.org/@traiter_echap_code_dist
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 ! 

	// 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;' "
		.$echap."</code></div>";
	} else {
		$echap = "<code class='spip_code' "
		."dir='ltr'>".$echap."</code>";

	$echap = str_replace("\t",
		"&nbsp; &nbsp; &nbsp; &nbsp; ", $echap);
	$echap = str_replace("  ", " &nbsp;", $echap);
	return $echap;
}

// Echapper les <cadre>...</ cadre> aka <frame>...</ frame>
// http://doc.spip.org/@traiter_echap_cadre_dist
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'");
// http://doc.spip.org/@traiter_echap_frame_dist
function traiter_echap_frame_dist($regs) {
	return traiter_echap_cadre_dist($regs);
}

// http://doc.spip.org/@traiter_echap_script_dist
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
// - 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
Fil's avatar
Fil a validé
function echappe_html($letexte, $source='', $no_transform=false,
	if (!is_string($letexte) or !strlen($letexte))
		return $letexte;
Fil's avatar
Fil a validé
	if (!$preg) $preg = ',<(html|code|cadre|frame|script)'
Fil's avatar
Fil a validé
	if (preg_match_all(
Fil's avatar
Fil a validé
	$preg,
	$letexte, $matches, PREG_SET_ORDER))
	foreach ($matches as $regs) {
		// echappements tels quels ?
		if ($no_transform) {
			$echap = $regs[0];
		// sinon les traiter selon le cas
		else if (function_exists($f = 'traiter_echap_'.strtolower($regs[1])))
			$echap = $f($regs);
		else if (function_exists($f = $f.'_dist'))
			$echap = $f($regs);
Yannick Patois's avatar
Yannick Patois a validé

		$letexte = str_replace($regs[0],
			code_echappement($echap, $source, $no_transform),
Yannick Patois's avatar
Yannick Patois a validé
	}
	if (strpos($letexte, "<math>") !== false) {
Fil's avatar
Fil a validé
		include_spip('inc/math');
		$letexte = traiter_math($letexte, $source);
ARNO*'s avatar
ARNO* a validé
	}

	// 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);
	}
Yannick Patois's avatar
Yannick Patois a validé
}

// 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 = "") {
		# spip_log(htmlspecialchars($letexte));  ## pour les curieux
		',<(span|div) class=[\'"]base64'.$source.'[\'"]\s.*>\s*</\1>,UmsS',
		$letexte, $regs, PREG_SET_ORDER)) {
			foreach ($regs as $reg) {
				$rempl = base64_decode(extraire_attribut($reg[0], 'title'));
				if ($filtre) $rempl = $filtre($rempl);
				$letexte = str_replace($reg[0], $rempl, $letexte);
			}
// Reinserer le javascript de confiance (venant des modeles)

Christian Lefebvre's avatar
Christian Lefebvre a validé
// http://doc.spip.org/@echappe_retour_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))
			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);
	$texte = str_replace(array('}','{'), '', $texte);
	$texte = preg_replace(",(^|\r)\|.*\|\r,s", "\r", $texte);
// http://doc.spip.org/@couper
function couper($texte, $taille=50, $suite = '&nbsp;(...)') {
	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
	$texte = str_replace("\r", "\n", $texte);

	// sauts de ligne et paragraphes
cerdic's avatar
cerdic a validé
	$texte = preg_replace("/\n\n+/", "\r", $texte);
	$texte = preg_replace("/<(p|br)( [^>]*)?".">/", "\r", $texte);
	$texte = preg_replace("/(^|\r|\n)(-[-#\*]*|_ )/", "\r", $texte);
	// 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);
Fil's avatar
Fil a validé
	$long = spip_substr($texte, 0, max($taille-4,1));
	$court = preg_replace("/([^\s][\s]+)[^\s]*\n?$/", "\\1", $long);
Fil's avatar
Fil a validé
	if (spip_strlen($court) < max(0.75 * $taille,2)) {
Fil's avatar
Fil a validé
		$long = spip_substr($texte, 0, $taille);
		$texte = preg_replace("/([^\s][\s]+)[^\s]*\n?$/", "\\1", $long);
		// encore trop court ? couper au caractere
Fil's avatar
Fil a validé
		if (spip_strlen($texte) < 0.75 * $taille)
Fil's avatar
Fil a validé
	if (strpos($texte, "\n"))	// la fin est encore la : c'est qu'on n'a pas de texte de suite
	$texte = preg_replace("/\r+/", "\n\n", $texte);
Fil's avatar
Fil a validé
	// supprimer l'eventuelle entite finale mal coupee
	$texte = preg_replace('/&#?[a-z0-9]*$/S', '', $texte);
Fil's avatar
Fil a validé

Fil's avatar
Fil a validé
	return quote_amp(trim($texte)).$points;
Yannick Patois's avatar
Yannick Patois a validé
}

Yannick Patois's avatar
Yannick Patois a validé

// 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", "&lt;\\1", $source);
Yannick Patois's avatar
Yannick Patois a validé
}
*/

// afficher joliment les <script>
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;
}
Christian Lefebvre's avatar
Christian Lefebvre a validé
// http://doc.spip.org/@protege_js_modeles
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)
cerdic's avatar
cerdic a validé
				$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);
		}

// 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
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('<'.'%', '&lt;%', $t);
	
		// echapper le php
		$t = str_replace('<'.'?', '&lt;?', $t);
	
		// echapper le < script language=php >
		$t = preg_replace(',<(script\b[^>]+\blanguage\b[^\w>]+php\b),UimsS', '&lt;\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', '&lt;\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);
Yannick Patois's avatar
Yannick Patois a validé

// Securite : utiliser SafeHTML s'il est present dans ecrire/safehtml/
// http://doc.spip.org/@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
	if (!isset($safehtml))
		$safehtml = charger_fonction('safehtml', 'inc');
	if ($safehtml)
		$t = $safehtml($t);
	return interdire_scripts($t); // interdire le php (2 precautions)
// avec protection prealable des balises HTML et SPIP

Christian Lefebvre's avatar
Christian Lefebvre a validé
// http://doc.spip.org/@typo
function typo($letexte, $echapper=true, $connect='') {
Yannick Patois's avatar
Yannick Patois a validé

	if (!$letexte) return $letexte;
	// Echapper les codes <html> etc
	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
		$letexte = interdire_scripts($letexte,true);
Christian Lefebvre's avatar
Christian Lefebvre a validé
// http://doc.spip.org/@corriger_typo
function corriger_typo($letexte) {

	// Plus vite !
	if (!$letexte) return $letexte;

Fil's avatar
Fil a validé
	$letexte = pipeline('pre_typo', $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);
			}
Antoine Pitrou's avatar
Antoine Pitrou a validé

		$letexte = $typographie($letexte);

		// Retablir les caracteres proteges
		$letexte = strtr($letexte, $illegal, $protege);

	}
Fil's avatar
Fil a validé
	$letexte = pipeline('post_typo', $letexte);
	# un message pour abs_url - on est passe en mode texte
	$GLOBALS['mode_abs_url'] = 'texte';

Yannick Patois's avatar
Yannick Patois a validé
}

// analyse des raccourcis issus de [TITRE->RACCOURCInnn] et connexes
define('_RACCOURCI_URL', ',^\s*(\w*?)\s*(\d+)(\?(.*?))?(#([^\s]*))?\s*$,S');
Christian Lefebvre's avatar
Christian Lefebvre a validé
// http://doc.spip.org/@typer_raccourci
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';

esj's avatar
esj a validé
	return ($pour == 'url') ? $lien : array($lien, $class, $texte, '');
Fil's avatar
Fil a validé
//
// Tableaux
//
// http://doc.spip.org/@traiter_tableau
Fil's avatar
Fil a validé
function traiter_tableau($bloc) {

	// Decouper le tableau en lignes
	preg_match_all(',([|].*)[|]\n,UmsS', $bloc, $regs, PREG_PATTERN_ORDER);
	$lignes = array();
	$debut_table = $summary = '';
	$l = 0;
Fil's avatar
Fil a validé
	// Traiter chaque ligne
	foreach ($regs[1] as $ligne) {
		$l ++;

		// Gestion de la premiere ligne :
		if ($l == 1) {
Fil's avatar
Fil a validé
		// - <caption> et summary dans la premiere ligne :
		//   || caption | summary || (|summary est optionnel)
			if (preg_match(',^\|\|([^|]*)(\|(.*))?$,sS', $ligne, $cap)) {
Fil's avatar
Fil a validé
				$l = 0;
Fil's avatar
Fil a validé
				if ($caption = trim($cap[1]))
					$debut_table .= "<caption>".$caption."</caption>\n";
Fil's avatar
Fil a validé
				$summary = ' summary="'.entites_html(trim($cap[3])).'"';
Fil's avatar
Fil a validé
			}
		// - <thead> sous la forme |{{titre}}|{{titre}}|
		//   Attention thead oblige a avoir tbody
			else if (preg_match(',^(\|([[:space:]]*(:?{{[^}]+}}[[:space:]]*)?|<))+$,sS',
Fil's avatar
Fil a validé
				$ligne, $thead)) {
			  	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";
Fil's avatar
Fil a validé
				$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);

Fil's avatar
Fil a validé
			// Pas de paragraphes dans les cellules
			$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"
Fil's avatar
Fil a validé
		. $debut_table
		. "<tbody>\n"
Fil's avatar
Fil a validé
		. "</tbody>\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;
				// d'abord traiter les descentes
				while ($niveau > $profond - $change_type) {
					$ajout .= $pile_li[$niveau];
					$ajout .= $pile_type[$niveau];
Fil's avatar
Fil a validé
					if (!$change_type)
						unset ($pile_li[$niveau]);
				// puis les identites (y compris en fin de descente)
				if ($niveau == $profond && !$change_type) {
Fil's avatar
Fil a validé
					$ajout .= $pile_li[$niveau];
				}

				// 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>";
				$ajout .= "<li$class_spip>";
				$pile_li[$profond] = "</li>";
Antoine Pitrou's avatar
Antoine Pitrou a validé
			else {
			$texte .= $ajout . $regs[2];
		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
// Sert aussi a nettoyer un texte qu'on veut mettre dans un <a> etc.
// TODO: gerer les modeles ?
// http://doc.spip.org/@supprime_img
function supprime_img($letexte, $message=NULL) {
	if ($message===NULL) $message = '(' . _T('img_indisponible') . ')';
	return preg_replace(',<(img|doc|emb)([0-9]+)(\|([^>]*))?'.'\s*/?'.'>,i',
// 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
Fil's avatar
Fil a validé
// 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 >
define('_RACCOURCI_MODELE_DEBUT', '@^' . _RACCOURCI_MODELE .'@is');
Christian Lefebvre's avatar
Christian Lefebvre a validé
// http://doc.spip.org/@traiter_modeles
function traiter_modeles($texte, $doublons=false, $echap='', $connect='') {
	// detecter les modeles (rapide)
	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)
			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]);
Fil's avatar
Fil a validé
			# hack articles_edit, breves_edit, indexation
			if ($doublons)
				$texte .= preg_replace(',[|][^|=]*,s',' ',$params);
Fil's avatar
Fil a validé
			# version normale
			else {
				$modele = inclure_modele($type, $id, $params, $lien, $connect);
Fil's avatar
Fil a validé

				// le remplacer dans le texte
				if ($modele !== false) {
					$modele = protege_js_modeles($modele);
					$rempl = code_echappement($modele, $echap);
Fil's avatar
Fil a validé
					$texte = substr($texte, 0, $a)
						. $rempl
						. substr($texte, $a+$cherche);
Fil's avatar
Fil a validé
				}
			if (((!_DIR_RESTREINT) OR ($doublons)) AND ($id) AND (in_array($type,array('doc','emb','img'))))
				$GLOBALS['doublons_documents_inclus'][] = $id;

//
// 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) {
	global $class_spip;

	$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>
		// 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(
		',(<p(>|\s[^>]*)>)\s*|\s*(</p[>[:space:]]),'.$u.'i', '\1\3',