From a75537e1eb0099875bc7b1e3b12db04b5e3c36e9 Mon Sep 17 00:00:00 2001
From: "Committo,Ergo:sum" <esj@rezo.net>
Date: Tue, 23 Sep 2008 08:19:22 +0000
Subject: [PATCH] =?UTF-8?q?Les=20fonctions=20malnomm=C3=A9s=20{{{calculer?=
 =?UTF-8?q?=5Furl=5F}}}*=20avaient=20=C3=A9t=C3=A9=20introduites=20pour=20?=
 =?UTF-8?q?palier=20les=20d=C3=A9ficiences=20du=20raccourci=20des=20gloses?=
 =?UTF-8?q?,=20ce=20qui=20amenait=20des=20comportements=20d=C3=A9tourn?=
 =?UTF-8?q?=C3=A9s=20et=20m=C3=AAme=20un=20=20bug=20(l'URL=20d'un=20site?=
 =?UTF-8?q?=20r=C3=A9f=C3=A9renc=C3=A9=20n'=C3=A9tait=20pas=20toujours=20r?=
 =?UTF-8?q?etourn=C3=A9e).=20Maintenant=20que=20le=20raccourci=20des=20glo?=
 =?UTF-8?q?ses=20est=20=C3=A9tendu,=20ces=20fonctions=20disparaissent=20et?=
 =?UTF-8?q?=20sont=20remplac=C3=A9es=20par=20une=20utilisation=20syst?=
 =?UTF-8?q?=C3=A9matique=20de=20l'entr=C3=A9e=20{{{=20titre=20}}}=20dans?=
 =?UTF-8?q?=20la=20description=20des=20tables=20retourn=C3=A9e=20par=20{{{?=
 =?UTF-8?q?=C2=A0trouver=5Ftable=20}}}=C2=A0depuis=20[12748]=20et=20[12749?=
 =?UTF-8?q?].?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Le fichier inc/lien.php qui ne contenait que ces fonctions est maintenant dévolu aux fonctions qui produisent des URL et qui auparavant figuraient dans inc/texte.php, lequel est maintenant plus conforme à son nom en traitant plus strictement les questions de typographie.
---
 ecrire/inc/lien.php  | 474 ++++++++++++++++++++++++++++++++++++-------
 ecrire/inc/texte.php | 411 +------------------------------------
 2 files changed, 407 insertions(+), 478 deletions(-)

diff --git a/ecrire/inc/lien.php b/ecrire/inc/lien.php
index 078cbe2224..a01432953c 100644
--- a/ecrire/inc/lien.php
+++ b/ecrire/inc/lien.php
@@ -14,99 +14,437 @@ if (!defined("_ECRIRE_INC_VERSION")) return;
 
 include_spip('base/abstract_sql');
 
-// http://doc.spip.org/@calculer_url_article_dist
-function calculer_url_article_dist($id, $texte='', $lien='', $connect='') {
-
-	$row = sql_fetsel('titre,lang', 'spip_articles', "id_article=$id",'','','','',$connect);
-	if (!trim($texte))
-		$texte = supprimer_numero($row['titre']);
-	if (!trim($texte))
-		$texte = _T('article') . $id;
-	return array($lien, 'spip_in', $texte, $row['lang']);
-}
 
-// http://doc.spip.org/@calculer_url_rubrique_dist
-function calculer_url_rubrique_dist($id, $texte='', $lien='', $connect='')
+//
+// Raccourcis liens [xxx->url]
+// Note : complique car c'est ici qu'on applique typo(),
+// et en plus on veut pouvoir les passer en pipeline
+//
+
+// Regexp des raccouris, aussi utilisee pour la fusion de sauvegarde Spip
+// Laisser passer des paires de crochets pour la balise multi
+// mais refuser plus d'imbrications ou de mauvaises imbrications
+// sinon les crochets ne peuvent plus servir qu'a ce type de raccourci
+define('_RACCOURCI_LIEN', ",\[([^][]*?([[]\w*[]][^][]*)*)->(>?)([^]]*)\],msS");
+
+// http://doc.spip.org/@expanser_liens
+function expanser_liens($letexte, $connect='')
 {
-	$row = sql_fetsel('titre,lang', 'spip_rubriques', "id_rubrique=$id",'','','','',$connect);
-	if (!trim($texte)) {
-		$texte = supprimer_numero($row['titre']);
-		if (!trim($texte)) $texte = $id;
+	$letexte = pipeline('pre_liens', $letexte);
+
+	$inserts = array();
+	if (preg_match_all(_RACCOURCI_LIEN, $letexte, $matches, PREG_SET_ORDER)) {
+		$i = 0;
+		foreach ($matches as $regs) {
+			$n = count($regs);
+			list($texte, $bulle, $hlang) = traiter_raccourci_lien_atts($regs[1]);
+			list ($lien, $class, $texte, $lang) =
+			  calculer_url($regs[$n-1], $texte, 'tout', $connect);
+			$inserts[++$i] = traiter_raccourci_lien_lang($lien, $class, $texte, $hlang, $lang, $bulle, $connect);
+
+			$letexte = str_replace($regs[0], "@@SPIP_ECHAPPE_LIEN_$i@@",
+				$letexte);
+		}
 	}
 
-	return array($lien, 'spip_in', $texte, $row['lang']);
+	$letexte = corriger_typo(traiter_modeles($letexte, false, false, $connect));
+	foreach ($inserts as $i => $insert) {
+		$letexte = str_replace("@@SPIP_ECHAPPE_LIEN_$i@@", $insert, $letexte);
+	}
+	return $letexte;
 }
 
-// http://doc.spip.org/@calculer_url_breve_dist
-function calculer_url_breve_dist($id, $texte='', $lien='', $connect='')
-{
-	$row = sql_fetsel('titre,lang', 'spip_breves', "id_breve=$id",'','','','',$connect);
-	if (!trim($texte)) {
-		$texte = supprimer_numero($row['titre']);
-		if (!trim($texte)) $texte = $id;
+// http://doc.spip.org/@traiter_raccourci_lien_lang
+function traiter_raccourci_lien_lang($lien, $class, $texte, $hlang, $lang, $bulle, $connect='')
+{		
+	// Si l'objet n'est pas de la langue courante, on ajoute hreflang
+	if (!$hlang AND $lang!=$GLOBALS['spip_lang'])
+		$hlang = $lang;
+	$lang = ($hlang ? ' hreflang="'.$hlang.'"' : '') . $bulle;
+
+	# ceci s'execute heureusement avant les tableaux et leur "|".
+	# Attention, le texte initial est deja echappe mais pas forcement
+	# celui retourne par calculer_url.
+
+	# Penser au cas [<imgXX|right>->URL], qui exige typo('<a>...</a>')
+	return typo('<a href="'.$lien
+		. ($class ? '" class="'.$class : '')
+		. '"'.$lang.'>'
+		. $texte.'</a>', true, $connect);
+}
+
+// Repere dans la partie texte d'un raccourci [texte->...]
+// la langue et la bulle eventuelles
+
+// http://doc.spip.org/@traiter_raccourci_lien_atts
+function traiter_raccourci_lien_atts($texte) {
+
+	$bulle = $hlang = '';
+	// title et hreflang donnes par le raccourci ?
+	if (preg_match(',^(.*?)([|]([^<>]*?))?([{]([a-z_]+)[}])?$,', $texte, $m)) {
+
+		$n =count($m);
+		// |infobulle ?
+		if ($n > 2) {
+			$bulle = ' title="'.texte_backend($m[3]).'"';
+			// {hreflang} ?
+			if ($n > 4) {
+			// si c'est un code de langue connu, on met un hreflang
+				if (traduire_nom_langue($m[5]) <> $m[5]) {
+					$hlang = $m[5];
+				}
+			// sinon c'est un italique
+				else {
+					$m[1] .= $m[4];
+				}
+			
+	// S'il n'y a pas de hreflang sous la forme {}, ce qui suit le |
+	// est peut-etre une langue
+			} else if (preg_match(',^[a-z_]+$,', $m[3])) {
+			// si c'est un code de langue connu, on met un hreflang
+			// mais on laisse le title (c'est arbitraire tout ca...)
+				if (traduire_nom_langue($m[3]) <> $m[3]) {
+				  $hlang = $m[3];
+				}
+			}
+		}
+		$texte = $m[1];
 	}
-	return array($lien, 'spip_in', $texte, $row['lang']);
+
+	return array(trim($texte), $bulle, $hlang);
 }
 
-// http://doc.spip.org/@calculer_url_auteur_dist
-function calculer_url_auteur_dist($id, $texte='', $lien='', $connect='')
-{
-	if ($texte=='') {
-		$row = sql_fetsel('nom', 'spip_auteurs', "id_auteur=$id",'','','','',$connect);
-		$texte = $row['nom'];
+// callback pour la fonction traiter_raccourci_liens()
+// http://doc.spip.org/@autoliens_callback
+function autoliens_callback($r) {
+	if (strlen($l = $r[1])) {
+		// reperer le protocole
+		$protocol = 'http';
+		if (preg_match(',^((https?):/*),S', $l, $m)) {
+			$l = substr($l, strlen($m[1]));
+			$protocol = $m[2];
+		}
+		// valider le nom de domaine
+		if (preg_match(
+		'/^(?:[^\W_]((?:[^\W_]|-){0,61}[^\W_,])?\.)+[a-z]{2,6}\b/Si', $l)) {
+			// supprimer les ponctuations a la fin d'une URL
+			preg_match('/^(.*?)([,.;?]?)$/', $l, $k);
+			$l = inserer_attribut(
+				expanser_liens('[->'.$protocol.'://'.$k[1].']'),
+			'rel', 'nofollow')
+			.$k[2];
+			// si le texte ne contenait pas le 'http:' on le supprime aussi
+			if (!$m)
+				$l = str_replace('>http://', '>', $l);
+			return $l;
+		}
 	}
-	return array($lien, 'spip_in', $texte); # pas de hreflang
+	return $r[0];
+}
+
+// extraire les liens ecrits en mode texte brut
+// http://doc.spip.org/@traiter_raccourci_liens
+function traiter_raccourci_liens($texte) {
+	return preg_replace_callback(
+	';\[[^\[\]]*(?:<-|->).*?\]|<a\b.*?</a\b|<.*?>|'
+	.'((?:https?:/|www\.)[^"\'\s\[\]\}\)<>]*);imsS',
+	'autoliens_callback', $texte);
+	return $texte;
 }
 
-// http://doc.spip.org/@calculer_url_mot_dist
-function calculer_url_mot_dist($id, $texte='', $lien='', $connect='')
+// 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;
+}
+
+// Fonction pour les champs chapo commencant par =,  redirection qui peut etre:
+// 1. un raccourci Spip habituel (premier If) [texte->TYPEnnn]
+// 2. un ultra raccourci TYPEnnn voire nnn (article) (deuxieme If)
+// 3. une URL std
+// renvoie une tableau structure comme ci-dessus mais sans calcul d'URL
+// (cf fusion de sauvegardes)
+
+define('_RACCOURCI_CHAPO', ',^(\W*)(\W*)(\w*\d+([?#].*)?)$,');
+
+// http://doc.spip.org/@chapo_redirige
+function chapo_redirige($chapo, $url=false)
 {
-	if (!trim($texte)) {
-		$row = sql_fetsel('titre', 'spip_mots', "id_mot=$id",'','','','',$connect);
-		$texte = supprimer_numero($row['titre']);
-		if (!trim($texte)) $texte = $id;
+	if (!preg_match(_RACCOURCI_LIEN, $chapo, $m))
+		if (!preg_match(_RACCOURCI_CHAPO, $chapo, $m))
+			return $chapo;
+	return !$url ? $m[3] : calculer_url($m[3]);
+}
+
+// Ne pas afficher le chapo si article virtuel
+// http://doc.spip.org/@nettoyer_chapo
+function nettoyer_chapo($chapo){
+	return (substr($chapo,0,1) == "=") ? '' : $chapo;
+}
+
+// http://doc.spip.org/@chapo_redirigetil
+function chapo_redirigetil($chapo) { return $chapo && $chapo[0] == '=';}
+
+// Cherche un lien du type [->raccourci 123]
+// associe a une fonction generer_url_raccourci() definie explicitement 
+// ou implicitement par le jeu de type_urls courant.
+//
+// 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='') {
+	if ($match = typer_raccourci($ref)) {
+		@list($type,,$id,,$args,,$ancre) = $match;
+# attention dans le cas des sites le lien doit pointer non pas sur
+# la page locale du site, mais directement sur le site lui-meme
+		if ($type == 'site')
+			$url = sql_getfetsel('url_site', 'spip_syndic', "id_syndic=$id",'','','','',$connect);
+		else $url = generer_url_entite($id,$type,$args,$ancre,	$connect ? $connect : NULL);
+		if ($url) 
+			return ($pour === 'url')
+			? $url
+			: calculer_url_lien($type, $id, $url, $texte, $pour, $connect);
 	}
-	return array($lien, 'spip_in', $texte);
+	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/@calculer_url_document_dist
-function calculer_url_document_dist($id, $texte='', $lien='', $connect='')
+// analyse des raccourcis issus de [TITRE->RACCOURCInnn] et connexes
+
+define('_RACCOURCI_URL', ',^\s*(\w*?)\s*(\d+)(\?(.*?))?(#([^\s]*))?\s*$,S');
+
+// 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;
+}
+
+function calculer_url_lien($type, $id, $url, $texte, $pour, $connect)
 {
-	if ($texte=='') {
-		$row = sql_fetsel('titre,fichier', 'spip_documents', "id_document=$id",'','','','',$connect);
-
-		$texte = $row['titre'];
-		if (!trim($texte))
-			$texte = preg_replace(",^.*/,","",$row['fichier']);
-		if (!trim($texte))
-		    $texte = $id;
+	$trouver_table = charger_fonction('trouver_table', 'base');
+	$desc = $trouver_table(table_objet($type));
+	$lang = '';
+	if ($desc AND $s = $desc['titre']) {
+		$_id = $desc['key']['PRIMARY KEY'];
+		$t = $desc['table'];
+		$r = sql_fetsel($s, $t, "$_id=$id", '','','','',$connect);
+		$texte = trim($texte);
+		if ($r AND !$texte) {
+			$texte = supprimer_numero($r['titre']);
+			if (!$texte) $texte = $r['surnom'];
+			if (!$texte) $texte = $id;
+			$lang = $r['lang'];
+		}
+		$style = 'spip_in';
+	} else $style =  'spip_out';
+
+	return ($pour=='titre') ? $texte : array($url, $style, $texte, $lang);
+}
+
+// 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');
+
+// 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)
+
+			$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);
+			# version normale
+			else {
+				$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);
+					$texte = substr($texte, 0, $a)
+						. $rempl
+						. 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 array($lien, 'spip_in', $texte); # pas de hreflang
+
+	return $texte;
 }
 
-// http://doc.spip.org/@calculer_url_site_dist
-function calculer_url_site_dist($id, $texte='', $lien='', $connect='')
+//
+// Raccourcis ancre [#ancre<-]
+//
+
+define('_RACCOURCI_ANCRE', "|\[#?([^][]*)<-\]|S");
+
+// http://doc.spip.org/@traiter_raccourci_ancre
+function traiter_raccourci_ancre($letexte)
 {
-	# attention dans le cas des sites le lien pointe non pas sur
-	# la page locale du site, mais directement sur le site lui-meme
-	$row =sql_fetsel('nom_site,url_site', 'spip_syndic', "id_syndic=$id",'','','','',$connect);
-	if ($row) {
-		$lien = $row['url_site'];
-		if (!trim($texte))
-			$texte = supprimer_numero($row['nom_site']);
-		if (!trim($texte)) $texte = $id;
+	if (preg_match_all(_RACCOURCI_ANCRE, $letexte, $m, PREG_SET_ORDER))
+	foreach ($m as $regs)
+		$letexte = str_replace($regs[0],
+		'<a name="'.entites_html($regs[1]).'"></a>', $letexte);
+	return $letexte;
+}
+
+//
+// Raccourcis automatiques [?SPIP] vers un glossaire
+// Wikipedia par defaut, avec ses contraintes techniques
+// cf. http://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Conventions_sur_les_titres
+
+define('_RACCOURCI_GLOSSAIRE', "|\[\?+\s*([^][<>]+)\]|S");
+
+// http://doc.spip.org/@traiter_raccourci_glossaire
+function traiter_raccourci_glossaire($letexte)
+{
+	if (!preg_match_all(_RACCOURCI_GLOSSAIRE, $letexte, $m, PREG_SET_ORDER))
+		return $letexte;
+
+	include_spip('inc/charsets');
+
+	foreach ($m as $regs) {
+		// Eviter les cas particulier genre "[?!?]"
+		// et isoler le lexeme a gloser de ses accessoires
+		// (#:url du glossaire, | bulle d'aide, {} lang)
+
+		if (preg_match(',^([^|#{]*\w[^|#{]*)([^#]*)(#([^|{}]*))?(.*)$,', $regs[1], $r)) {
+
+			list($terme, $bulle, $hlang) = traiter_raccourci_lien_atts($r[1] . $r[2] . $r[5]);
+
+			$terme = unicode2charset(charset2unicode($terme), 'utf-8');
+			
+			if ($r[4] AND function_exists($f = 'glossaire_' . $r[4]))
+				$glose = $f($terme);
+			else $glose  = glossaire_std($terme);
+			$ref = traiter_raccourci_lien_lang($glose, 'spip_glossaire', $terme, $hlang, '', $bulle);
+			$letexte = str_replace($regs[0], $ref, $letexte);
+		}
 	}
-	return array($lien, 'spip_out', $texte, $row['lang']);
+	return $letexte;
 }
 
-// http://doc.spip.org/@calculer_url_forum_dist
-function calculer_url_forum_dist($id, $texte='', $lien='', $connect='')
+function glossaire_std($terme)
 {
-	if (!trim($texte)) {
-		$row = sql_fetsel('titre', 'spip_forum', "id_forum=$id AND statut='publie'",'','','','',$connect);
-		$texte = $row['titre'];
-		if (!trim($texte)) $texte = $id;
+	global $url_glossaire_externe;
+	static $pcre = NULL;
+
+	if ($pcre === NULL) {
+		$pcre = isset($GLOBALS['meta']['pcre_u']) 
+		? $GLOBALS['meta']['pcre_u']
+		  : '';
+		if (strpos($url_glossaire_externe, "%s") === false)
+			$url_glossaire_externe .= '%s';
 	}
-	return array($lien, 'spip_in', $texte); # pas de hreflang
+
+	$glosateur = str_replace("@lang@",
+				$GLOBALS['spip_lang'],
+				$GLOBALS['url_glossaire_externe']);
+
+	$terme = rawurlencode(preg_replace(',\s+,'.$pcre, '_', $terme));
+	
+	return  str_replace("%s", $terme, $glosateur);
 }
+
 ?>
diff --git a/ecrire/inc/texte.php b/ecrire/inc/texte.php
index 19da34a42d..139c7f3b53 100644
--- a/ecrire/inc/texte.php
+++ b/ecrire/inc/texte.php
@@ -14,6 +14,7 @@ if (!defined("_ECRIRE_INC_VERSION")) return;
 
 include_spip('inc/filtres');
 include_spip('inc/lang');
+include_spip('inc/lien');
 
 //
 // Gerer les variables de personnalisation, qui peuvent provenir
@@ -124,12 +125,6 @@ define('_BALISES_BLOCS',
 	.'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
@@ -314,28 +309,6 @@ function echappe_retour_modeles($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 = '&nbsp;(...)') {
@@ -602,83 +575,6 @@ function corriger_typo($letexte) {
 }
 
 
-// analyse des raccourcis issus de [TITRE->RACCOURCInnn] et connexes
-
-define('_RACCOURCI_URL', ',^\s*(\w*?)\s*(\d+)(\?(.*?))?(#([^\s]*))?\s*$,S');
-
-// 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() definie explicitement 
-// ou implicitement par le jeu de type_urls courant.
-//
-// 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='') {
-	if ($match = typer_raccourci($ref)) {
-		@list($type,,$id,,$args,,$ancre) = $match;
-		$r = generer_url_entite($id,$type,$args,$ancre,	$connect ? $connect : NULL);
-		if ($r) {
-			if ($pour === 'url') return $r;
-			include_spip('inc/lien');
-			$g = 'calculer_url_' . $type;
-			if (!(function_exists($g) OR function_exists($g .= '_dist')))
-				$r = array($r, '', $texte);
-			else $r = $g($id, $texte, $r, $connect);
-			return ($pour=='tout') ? $r : $r[2];
-		}
-	}
-	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, '');
-}
 
 //
 // Tableaux
@@ -913,79 +809,6 @@ function supprime_img($letexte, $message=NULL) {
 		$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');
-
-// 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)
-
-			$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);
-			# version normale
-			else {
-				$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);
-					$texte = substr($texte, 0, $a)
-						. $rempl
-						. 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
@@ -1036,200 +859,6 @@ function paragrapher($letexte, $forcer=true) {
 	return $letexte;
 }
 
-//
-// Raccourcis ancre [#ancre<-]
-//
-
-define('_RACCOURCI_ANCRE', "|\[#?([^][]*)<-\]|S");
-
-// http://doc.spip.org/@traiter_raccourci_ancre
-function traiter_raccourci_ancre($letexte)
-{
-	if (preg_match_all(_RACCOURCI_ANCRE, $letexte, $m, PREG_SET_ORDER))
-	foreach ($m as $regs)
-		$letexte = str_replace($regs[0],
-		'<a name="'.entites_html($regs[1]).'"></a>', $letexte);
-	return $letexte;
-}
-
-//
-// Raccourcis automatiques [?SPIP] vers un glossaire
-// Wikipedia par defaut, avec ses contraintes techniques
-// cf. http://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Conventions_sur_les_titres
-
-define('_RACCOURCI_GLOSSAIRE', "|\[\?+\s*([^][<>]+)\]|S");
-
-// http://doc.spip.org/@traiter_raccourci_glossaire
-function traiter_raccourci_glossaire($letexte)
-{
-	if (!preg_match_all(_RACCOURCI_GLOSSAIRE, $letexte, $m, PREG_SET_ORDER))
-		return $letexte;
-
-	include_spip('inc/charsets');
-
-	foreach ($m as $regs) {
-		// Eviter les cas particulier genre "[?!?]"
-		// et isoler le lexeme a gloser de ses accessoires
-		// (#:url du glossaire, | bulle d'aide, {} lang)
-
-		if (preg_match(',^([^|#{]*\w[^|#{]*)([^#]*)(#([^|{}]*))?(.*)$,', $regs[1], $r)) {
-
-			list($terme, $bulle, $hlang) = traiter_raccourci_lien_atts($r[1] . $r[2] . $r[5]);
-
-			$terme = unicode2charset(charset2unicode($terme), 'utf-8');
-			
-			if ($r[4] AND function_exists($f = 'glossaire_' . $r[4]))
-				$glose = $f($terme);
-			else $glose  = glossaire_std($terme);
-			$ref = traiter_raccourci_lien_lang($glose, 'spip_glossaire', $terme, $hlang, '', $bulle);
-			$letexte = str_replace($regs[0], $ref, $letexte);
-		}
-	}
-	return $letexte;
-}
-
-function glossaire_std($terme)
-{
-	global $url_glossaire_externe;
-	static $pcre = NULL;
-
-	if ($pcre === NULL) {
-		$pcre = isset($GLOBALS['meta']['pcre_u']) 
-		? $GLOBALS['meta']['pcre_u']
-		  : '';
-		if (strpos($url_glossaire_externe, "%s") === false)
-			$url_glossaire_externe .= '%s';
-	}
-
-	$glosateur = str_replace("@lang@",
-				$GLOBALS['spip_lang'],
-				$GLOBALS['url_glossaire_externe']);
-
-	$terme = rawurlencode(preg_replace(',\s+,'.$pcre, '_', $terme));
-	
-	return  str_replace("%s", $terme, $glosateur);
-}
-
-//
-// Raccourcis liens [xxx->url]
-// Note : complique car c'est ici qu'on applique typo(),
-// et en plus on veut pouvoir les passer en pipeline
-//
-
-// Regexp des raccouris, aussi utilisee pour la fusion de sauvegarde Spip
-// Laisser passer des paires de crochets pour la balise multi
-// mais refuser plus d'imbrications ou de mauvaises imbrications
-// sinon les crochets ne peuvent plus servir qu'a ce type de raccourci
-define('_RACCOURCI_LIEN', ",\[([^][]*?([[]\w*[]][^][]*)*)->(>?)([^]]*)\],msS");
-
-// http://doc.spip.org/@expanser_liens
-function expanser_liens($letexte, $connect='')
-{
-	$letexte = pipeline('pre_liens', $letexte);
-
-	$inserts = array();
-	if (preg_match_all(_RACCOURCI_LIEN, $letexte, $matches, PREG_SET_ORDER)) {
-		$i = 0;
-		foreach ($matches as $regs) {
-			$n = count($regs);
-			list($texte, $bulle, $hlang) = traiter_raccourci_lien_atts($regs[1]);
-			list ($lien, $class, $texte, $lang) =
-			  calculer_url($regs[$n-1], $texte, 'tout', $connect);
-			$inserts[++$i] = traiter_raccourci_lien_lang($lien, $class, $texte, $hlang, $lang, $bulle, $connect);
-
-			$letexte = str_replace($regs[0], "@@SPIP_ECHAPPE_LIEN_$i@@",
-				$letexte);
-		}
-	}
-
-	$letexte = corriger_typo(traiter_modeles($letexte, false, false, $connect));
-	foreach ($inserts as $i => $insert) {
-		$letexte = str_replace("@@SPIP_ECHAPPE_LIEN_$i@@", $insert, $letexte);
-	}
-	return $letexte;
-}
-
-// http://doc.spip.org/@traiter_raccourci_lien_lang
-function traiter_raccourci_lien_lang($lien, $class, $texte, $hlang, $lang, $bulle, $connect='')
-{		
-	// Si l'objet n'est pas de la langue courante, on ajoute hreflang
-	if (!$hlang AND $lang!=$GLOBALS['spip_lang'])
-		$hlang = $lang;
-	$lang = ($hlang ? ' hreflang="'.$hlang.'"' : '') . $bulle;
-
-	# ceci s'execute heureusement avant les tableaux et leur "|".
-	# Attention, le texte initial est deja echappe mais pas forcement
-	# celui retourne par calculer_url.
-
-	# Penser au cas [<imgXX|right>->URL], qui exige typo('<a>...</a>')
-	return typo('<a href="'.$lien
-		. ($class ? '" class="'.$class : '')
-		. '"'.$lang.'>'
-		. $texte.'</a>', true, $connect);
-}
-
-// Repere dans la partie texte d'un raccourci [texte->...]
-// la langue et la bulle eventuelles
-
-// http://doc.spip.org/@traiter_raccourci_lien_atts
-function traiter_raccourci_lien_atts($texte) {
-
-	$bulle = $hlang = '';
-	// title et hreflang donnes par le raccourci ?
-	if (preg_match(',^(.*?)([|]([^<>]*?))?([{]([a-z_]+)[}])?$,', $texte, $m)) {
-
-		$n =count($m);
-		// |infobulle ?
-		if ($n > 2) {
-			$bulle = ' title="'.texte_backend($m[3]).'"';
-			// {hreflang} ?
-			if ($n > 4) {
-			// si c'est un code de langue connu, on met un hreflang
-				if (traduire_nom_langue($m[5]) <> $m[5]) {
-					$hlang = $m[5];
-				}
-			// sinon c'est un italique
-				else {
-					$m[1] .= $m[4];
-				}
-			
-	// S'il n'y a pas de hreflang sous la forme {}, ce qui suit le |
-	// est peut-etre une langue
-			} else if (preg_match(',^[a-z_]+$,', $m[3])) {
-			// si c'est un code de langue connu, on met un hreflang
-			// mais on laisse le title (c'est arbitraire tout ca...)
-				if (traduire_nom_langue($m[3]) <> $m[3]) {
-				  $hlang = $m[3];
-				}
-			}
-		}
-		$texte = $m[1];
-	}
-
-	return array($texte, $bulle, $hlang);
-}
-
-// Fonction pour les champs chapo commencant par =,  redirection qui peut etre:
-// 1. un raccourci Spip habituel (premier If) [texte->TYPEnnn]
-// 2. un ultra raccourci TYPEnnn voire nnn (article) (deuxieme If)
-// 3. une URL std
-// renvoie une tableau structure comme ci-dessus mais sans calcul d'URL
-// (cf fusion de sauvegardes)
-
-define('_RACCOURCI_CHAPO', ',^(\W*)(\W*)(\w*\d+([?#].*)?)$,');
-
-// http://doc.spip.org/@chapo_redirige
-function chapo_redirige($chapo, $url=false)
-{
-	if (!preg_match(_RACCOURCI_LIEN, $chapo, $m))
-		if (!preg_match(_RACCOURCI_CHAPO, $chapo, $m))
-			return $chapo;
-	return !$url ? $m[3] : calculer_url($m[3]);
-}
-
-// http://doc.spip.org/@chapo_redirigetil
-function chapo_redirigetil($chapo) { return $chapo && $chapo[0] == '=';}
-
 // http://doc.spip.org/@traiter_poesie
 function traiter_poesie($letexte)
 {
@@ -1248,44 +877,6 @@ function traiter_poesie($letexte)
 	return $letexte;
 }
 
-// callback pour la fonction traiter_raccourci_liens()
-// http://doc.spip.org/@autoliens_callback
-function autoliens_callback($r) {
-	if (strlen($l = $r[1])) {
-		// reperer le protocole
-		$protocol = 'http';
-		if (preg_match(',^((https?):/*),S', $l, $m)) {
-			$l = substr($l, strlen($m[1]));
-			$protocol = $m[2];
-		}
-		// valider le nom de domaine
-		if (preg_match(
-		'/^(?:[^\W_]((?:[^\W_]|-){0,61}[^\W_,])?\.)+[a-z]{2,6}\b/Si', $l)) {
-			// supprimer les ponctuations a la fin d'une URL
-			preg_match('/^(.*?)([,.;?]?)$/', $l, $k);
-			$l = inserer_attribut(
-				expanser_liens('[->'.$protocol.'://'.$k[1].']'),
-			'rel', 'nofollow')
-			.$k[2];
-			// si le texte ne contenait pas le 'http:' on le supprime aussi
-			if (!$m)
-				$l = str_replace('>http://', '>', $l);
-			return $l;
-		}
-	}
-	return $r[0];
-}
-
-// extraire les liens ecrits en mode texte brut
-// http://doc.spip.org/@traiter_raccourci_liens
-function traiter_raccourci_liens($texte) {
-	return preg_replace_callback(
-	';\[[^\[\]]*(?:<-|->).*?\]|<a\b.*?</a\b|<.*?>|'
-	.'((?:https?:/|www\.)[^"\'\s\[\]\}\)<>]*);imsS',
-	'autoliens_callback', $texte);
-	return $texte;
-}
-
 // Harmonise les retours chariots et mange les paragraphes html
 // http://doc.spip.org/@traiter_retours_chariots
 function traiter_retours_chariots($letexte) {
-- 
GitLab