From 84bbf4fa88d96942a8156de7ba27bfc953a9f506 Mon Sep 17 00:00:00 2001
From: Fil <fil@rezo.net>
Date: Fri, 27 Aug 2004 04:27:03 +0000
Subject: [PATCH] =?UTF-8?q?encore=20beaucoup=20de=20modifs,=20je=20ne=20d?=
 =?UTF-8?q?=C3=A9taille=20pas,=20car=20elles=20sont=20surtout=20du=20fond?=
 =?UTF-8?q?=20et=20des=20suites=20de=20discussions=20sur=20spip-dev=20:?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* le debogueur devient de plus en plus riche et subtil (n'affiche plus à tout vent les erreurs mysql, par exemple, mais seulement aux admins)

* nettoayge du code des forums

* tentative de résoudre le bug de ecrire_fichier sous windows

* En matière de boucles et balises, l'ajout de id_thread dans la table spip_forum donne tout de suite le critère {id_thread} qui va bien pour retrouver le pied d'une discussion

etc.

Il va falloir mettre à jour NOUVEAUTES et TODO :)
---
 ecrire/admin_vider.php3    |  10 +-
 ecrire/forum_envoi.php3    |   3 +
 ecrire/inc_db_mysql.php3   |   1 -
 ecrire/inc_forum.php3      |  35 +++-
 ecrire/inc_invalideur.php3 |  29 ++-
 ecrire/inc_majbase.php3    |   8 +
 ecrire/inc_serialbase.php3 |   1 +
 ecrire/inc_version.php3    |  67 +++++--
 inc-admin.php3             |  93 ++++++++-
 inc-arg-squel.php3         |  15 +-
 inc-cache.php3             |   6 +-
 inc-calcul-squel.php3      |  41 ++--
 inc-calcul.php3            |  37 ++--
 inc-calcul_html4.php       |  46 -----
 inc-calcul_mysql3.php      | 190 +++++++-----------
 inc-debug-squel.php3       |  56 ------
 inc-form-squel.php3        | 119 +----------
 inc-forum.php3             | 399 +++++++++++++++++++++++++++----------
 inc-html-squel.php3        |  17 +-
 inc-index-squel.php3       |   2 +-
 inc-messforum.php3         |  14 +-
 inc-public-global.php3     |  13 +-
 inc-public.php3            |  26 +--
 inc-reqsql-squel.php3      |   3 +-
 inc-vrac-squel.php3        |   6 +-
 25 files changed, 674 insertions(+), 563 deletions(-)
 delete mode 100755 inc-debug-squel.php3

diff --git a/ecrire/admin_vider.php3 b/ecrire/admin_vider.php3
index 626b5e84d3..fd700fad6d 100644
--- a/ecrire/admin_vider.php3
+++ b/ecrire/admin_vider.php3
@@ -78,11 +78,13 @@ echo _L('Taille du r&eacute;pertoire cache')."</FONT></B></TD></TR>";
 
 echo "<TR><TD class='serif'>";
 
-$row = spip_fetch_array(spip_query("SELECT SUM(taille) FROM spip_caches"));
-if ($row[0]>0)
+list ($taille) = spip_fetch_array(spip_query(
+"SELECT SUM(taille) FROM spip_caches WHERE type='t'"));
+
+if ($taille>0) {
 	$info = _L("La taille du cache est actuellement de "
-	.taille_en_octets($row[0]).".");
-else
+	.taille_en_octets($taille).".");
+} else
 	$info = _L('Le cache est vide.');
 
 echo "<p align='justify'><b>$info</b></p>\n";
diff --git a/ecrire/forum_envoi.php3 b/ecrire/forum_envoi.php3
index d6341805b6..5b65b45538 100644
--- a/ecrire/forum_envoi.php3
+++ b/ecrire/forum_envoi.php3
@@ -2,6 +2,7 @@
 
 include ("inc.php3");
 include_ecrire ("inc_barre.php3");
+include_ecrire ("inc_forum.php3");
 
 
 if ($modif_forum != "oui") $titre_message = ereg_replace("^([^>])", "> \\1", $titre_message);
@@ -15,6 +16,8 @@ if ($valider_forum AND ($statut!='')) {
 	"VALUES (\"$titre_message\", \"$texte\", NOW(), \"$nom_site\", \"$url_site\", \"$statut\", \"$connect_id_auteur\", \"$nom\", '$connect_email', '$id_rubrique', '$id_parent', '$id_article', '$id_breve', '$id_message', '$id_syndic')";
 	$result = spip_query($query);
 
+	calculer_threads();
+
 	if ($id_message > 0) {
 		$query = "UPDATE spip_auteurs_messages SET vu = 'non' WHERE id_message='$id_message'";
 		$result = spip_query($query);
diff --git a/ecrire/inc_db_mysql.php3 b/ecrire/inc_db_mysql.php3
index 81294258b9..d6a6f21373 100644
--- a/ecrire/inc_db_mysql.php3
+++ b/ecrire/inc_db_mysql.php3
@@ -175,6 +175,5 @@ function calcul_mysql_in($val, $valeurs, $not='') {
 			calcul_mysql_in($val, $valeurs, $not));
     }
 }
-  
 
 ?>
diff --git a/ecrire/inc_forum.php3 b/ecrire/inc_forum.php3
index 422df1633d..bad51a523b 100644
--- a/ecrire/inc_forum.php3
+++ b/ecrire/inc_forum.php3
@@ -66,5 +66,38 @@ function calcul_index_forum($id_article, $id_breve, $id_rubrique, $id_syndic) {
 	if ($id_syndic) return 's'.$id_syndic;
 }
 
- 
+//
+// Recalculer tous les threads
+//
+function calculer_threads() {
+	// fixer les id_thread des debuts de discussion
+	spip_query("UPDATE spip_forum SET id_thread=id_forum
+	WHERE id_parent=0");
+
+	// reparer les messages qui n'ont pas l'id_secteur de leur parent
+	do {
+		$discussion = "0";
+		$precedent = 0;
+		$r = spip_query("SELECT fille.id_forum AS id,
+		maman.id_thread AS thread
+		FROM spip_forum AS fille, spip_forum AS maman
+		WHERE fille.id_parent = maman.id_forum
+		AND fille.id_thread <> maman.id_thread
+		ORDER BY thread");
+		while (list($id, $thread) = spip_fetch_array($r)) {
+			if ($thread == $precedent)
+				$discussion .= ",$id";
+			else {
+				if ($precedent)
+					spip_query("UPDATE spip_forum SET id_thread=$precedent
+					WHERE id_forum IN ($discussion)");
+				$precedent = $thread;
+				$discussion = "$id";
+			}
+		}
+		spip_query("UPDATE spip_forum SET id_thread=$precedent
+		WHERE id_forum IN ($discussion)");
+	} while ($discussion != "0");
+}
+
 ?>
diff --git a/ecrire/inc_invalideur.php3 b/ecrire/inc_invalideur.php3
index 06aa8a68e5..693489ee19 100644
--- a/ecrire/inc_invalideur.php3
+++ b/ecrire/inc_invalideur.php3
@@ -62,26 +62,27 @@ function retire_cache($cache) {
 	if (preg_match(
 	"|^CACHE(/[0-9a-f])?(/[0-9]+)?/[^.][\-_\%0-9a-z]+\.[0-9a-f]+(\.gz)?$|i",
 	$cache)) {
-		@unlink($cache);		// supprimer le fichier
-		@unlink($cache.'.NEW');	// et le fichier compagnon s'il existe
+		// supprimer le fichier (avec spip_flock)
+		supprimer_fichier($cache);
+		// et le fichier compagnon s'il existe
+		@unlink($cache.'.NEW');
 	} else
 		spip_log("Impossible de retirer $cache");
 }
 
 // Supprimer les caches marques "x"
-function retire_caches($forcer = false) {
+function retire_caches($chemin_prioritaire = '') {
 	if ($GLOBALS['flag_ecrire']) return;
 
-	// marque comme fait
-	effacer_meta('invalider');
-	ecrire_metas();
-
-	// essayer d'eviter de faire le meme travail qu'un autre processus
-	// attendre maxi 3 secondes
-	spip_get_lock("invalidation", 4);
+	// inutile de ramer si tout est invalide, on n'est pas tout seul
+	$max = 30;
+	// mais recuperer en priorite notre chemin
+	if ($chemin_prioritaire)
+		$order = "ORDER BY fichier != '$chemin_prioritaire'";
 
 	// faire le boulot de suppression
-	$q = spip_query("SELECT DISTINCT fichier FROM spip_caches WHERE type='x'");
+	$q = spip_query("SELECT DISTINCT fichier FROM spip_caches
+	WHERE type='x' $order LIMIT 0,$max");
 	if ($n = @spip_num_rows($q)) {
 		spip_log ("Retire $n caches");
 		while (list($cache) = spip_fetch_array($q)) {
@@ -91,6 +92,12 @@ function retire_caches($forcer = false) {
 		spip_query("DELETE FROM spip_caches WHERE "
 		.calcul_mysql_in('fichier', join(',',$supprimes)) );
 	}
+
+	// marque comme fait
+	if (count($supprimes) < $max) {
+		effacer_meta('invalider');
+		ecrire_metas();
+	}
 }
 
 //
diff --git a/ecrire/inc_majbase.php3 b/ecrire/inc_majbase.php3
index e10129c329..2d89571677 100644
--- a/ecrire/inc_majbase.php3
+++ b/ecrire/inc_majbase.php3
@@ -878,6 +878,14 @@ function maj_base() {
 		maj_version(1.804);
 	}
 
+	if ($version_installee < 1.805) {
+		spip_query("ALTER TABLE spip_forum
+		ADD id_thread bigint(21) DEFAULT '0' NOT NULL");
+		include_ecrire('inc_forum.php3');
+		calculer_threads();
+		maj_version(1.805);
+	}
+
 	return true;
 }
 
diff --git a/ecrire/inc_serialbase.php3 b/ecrire/inc_serialbase.php3
index bba034153f..b8ff4291cd 100644
--- a/ecrire/inc_serialbase.php3
+++ b/ecrire/inc_serialbase.php3
@@ -256,6 +256,7 @@ $spip_syndic_articles_key = array(
 $spip_forum = array(
 		"id_forum"	=> "bigint(21) NOT NULL",
 		"id_parent"	=> "bigint(21) DEFAULT '0' NOT NULL",
+		"id_thread"	=> "bigint(21) DEFAULT '0' NOT NULL",
 		"id_rubrique"	=> "bigint(21) DEFAULT '0' NOT NULL",
 		"id_article"	=> "bigint(21) DEFAULT '0' NOT NULL",
 		"id_breve"	=> "bigint(21) DEFAULT '0' NOT NULL",
diff --git a/ecrire/inc_version.php3 b/ecrire/inc_version.php3
index 2219fe6508..3d43e550d9 100644
--- a/ecrire/inc_version.php3
+++ b/ecrire/inc_version.php3
@@ -202,10 +202,10 @@ if ($flag_ecrire) {
 // (utilise pour les modifs de la base de donnees)
 
 // version de la base
-$spip_version = 1.804;
+$spip_version = 1.805;
 
 // version de spip
-$spip_version_affichee = "1.8 alpha 5 CVS";
+$spip_version_affichee = "1.8 alpha 6 CVS";
 
 // version de spip / tag cvs
 if (ereg('Name: v(.*) ','$Name$', $regs)) $spip_version_affichee = $regs[1];
@@ -821,7 +821,8 @@ if (LOCK_UN!=3) {
 function test_flock ($dir, $fp=false) {
 	static $flock = array();
 	global $flag_flock;
-	if (!$flag_flock)
+	if (!$flag_flock
+	OR $os_serveur == 'windows') // sous win rename() plante avec fopen()
 		return false;
 
 	if (!$dir) $dir = '.';
@@ -853,20 +854,28 @@ function spip_flock($filehandle, $mode, $fichier) {
 
 	if (!test_flock($dir))
 		return true;
-	else
-		return @flock($filehandle, $mode);
+
+	$r = flock($filehandle, $mode);
+
+	// demande de verrou ==> risque de sleep ==> forcer la relecture de l'etat
+	if ($mode == LOCK_EX)
+		clearstatcache();
+
+	return $r;
 }
 
 function spip_file_get_contents ($fichier) {
 	if (substr($fichier, -3) != '.gz') {
-		if (function_exists('file_get_contents'))
-			return @file_get_contents($fichier);
+		if (function_exists('file_get_contents')
+		AND $os_serveur !='windows') # windows retourne ''
+			return @file_get_contents ($fichier);
 		else
 			return join('', file($fichier));
 	} else
 			return join('', gzfile($fichier));
 	
 }
+
 // options = array(
 // 'phpcheck' => 'oui' # verifier qu'on a bien du php
 // dezippe automatiquement les fichiers .gz
@@ -909,13 +918,17 @@ function lire_fichier ($fichier, &$contenu, $options=false) {
 // zippe les fichiers .gz
 function ecrire_fichier ($fichier, $contenu) {
 
+	// Ne rien faire si on est en preview ou si une erreur
+	// grave s'est presentee (compilation du squelette, MySQL, etc)
+	if ($GLOBALS['var_preview'] OR defined('spip_erreur_fatale'))
+		return;
+
 	// Ecriture dans un fichier temporaire
 	// dans le repertoire destination
 	preg_match('|(.*)/([^/]*)$|', $fichier, $match);
 	$dir = $match[1];
 	$fichiertmp = $dir.'/'
-	.uniqid(substr(md5($fichier),0,6).'-'
-	.@getmypid()).".tmp";
+	.uniqid(substr(md5($fichier),0,6).'-'.@getmypid()).".tmp";
 
 	$gzip = (substr($fichier, -3) == '.gz');
 
@@ -934,36 +947,52 @@ function ecrire_fichier ($fichier, $contenu) {
 	if ($ft = @$fopen($fichiertmp, 'wb')) {
 		// on en profite pour tester flock()
 		$flock = test_flock($dir, $ft);
-		$s = @$fputs($ft, $contenu);
+		$s = @$fputs($ft, $contenu, $a = strlen($contenu));
 		@$fclose($ft);
-		$ok = (strlen($contenu) == $s);
+		$ok = ($s == $a);
 	}
 
 	if ($ok) {
 		// ouvrir et obtenir un verrou sur le fichier destination
-		if ($fp = @fopen($fichier, 'a')) {
-			if ($flock) while (!spip_flock($fp, LOCK_EX, $fichier));
-
-			// recopier le temporaire
-			$ok = @rename($fichiertmp, $fichier);
+		if ($flock AND $fp = @fopen($fichier, 'a'))
+			while (!spip_flock($fp, LOCK_EX, $fichier));
 
-			// liberer le verrou
-			if ($flock) spip_flock($fp, LOCK_UN, $fichier);
+		// recopier le temporaire
+		$ok = @rename("./".$fichiertmp, "./".$fichier);
 
+		// liberer le verrou
+		if ($flock AND $fp) {
+			spip_flock($fp, LOCK_UN, $fichier);
 			@fclose($fp);
 		}
 	}
 
 
 	// en cas d'echec effacer le temporaire
-	if (!$ok)
+	if (!$ok) {
+		spip_log("echec ecriture fichier $fichier");
 		@unlink($fichiertmp);
+	}
 
 	#spip_log("$fputs $fichier ".spip_timer('ecrire_fichier'));
 
 	return $ok;
 }
 
+//
+// Supprimer un fichier en attendant d'abord de le spip_flock
+//
+function supprimer_fichier ($fichier) {
+	if (!@file_exists($fichier))
+		return false;
+
+	if ($fl = @fopen($fichier, 'r')) {
+		while(!spip_flock($fl, LOCK_EX));
+		spip_flock($fl, LOCK_UN);
+		@fclose($fl);
+		@unlink($fichier);
+	}
+}
 
 //
 // Lire les meta cachees
diff --git a/inc-admin.php3 b/inc-admin.php3
index 21d6f8f128..0c114f973b 100644
--- a/inc-admin.php3
+++ b/inc-admin.php3
@@ -9,7 +9,6 @@ define("_INC_ADMIN", "1");
 //
 // Afficher un bouton admin
 //
-
 function bouton_admin($titre, $lien) {
 	return "<li><a href='$lien' class='spip-admin-boutons'>$titre</a></li>\n";
 }
@@ -170,6 +169,9 @@ function spip_error_handler ($errno, $errmsg, $filename, $linenum, $vars) {
 	}
 }
 
+//
+// Si le code php produit des erreurs, on peut les afficher
+//
 function affiche_erreurs_execution_page() {
 	global $tableau_des_erreurs, $page_principale, $s;
 	echo "<div style='position: absolute; z-index: 1000;
@@ -191,4 +193,93 @@ function affiche_erreurs_execution_page() {
 	$GLOBALS['bouton_admin_debug'] = true;
 }
 
+//
+// Si une boucle cree des soucis, on peut afficher la requete fautive
+// avec son code d'erreur
+//
+function erreur_requete_boucle($query, $id_boucle, $type) {
+	global $auteur_session, $HTTP_COOKIE_VARS, $dir_ecrire;
+	include_ecrire("inc_presentation.php3");
+
+	// Drapeau pour interdire d'ecrire les fichiers dans le cache
+	define('spip_erreur_fatale', 'requete_boucle');
+
+	// Calmer le jeu avec MySQL (si jamais on est en saturation)
+	@touch($dir_ecrire.'data/mysql_out');	// pour spip_cron
+	@touch($dir_ecrire.'data/lock');		// lock hebergeur
+	spip_log('Erreur MySQL: on limite les acces quelques minutes');
+	$GLOBALS['bouton_admin_debug'] = true;
+
+	$erreur = spip_sql_error();
+	$errno = spip_sql_errno();
+	if (eregi('err(no|code):?[[:space:]]*([0-9]+)', $erreur, $regs))
+		$errsys = $regs[2];
+	else if (($errno == 1030 OR $errno <= 1026)
+	AND ereg('[^[:alnum:]]([0-9]+)[^[:alnum:]]', $erreur, $regs))
+		$errsys = $regs[1];
+
+	// Erreur systeme
+	if ($errsys > 0 AND $errsys < 200) {
+		$retour .= "<tt><br><br><blink>"
+		. _T('info_erreur_systeme', array('errsys'=>$errsys))
+		. "</blink><br>\n"
+		. _T('info_erreur_systeme2');
+		spip_log("Erreur systeme $errsys");
+	}
+	// Requete erronee
+	else {
+		$retour .= "<tt><blink>&lt;BOUCLE".$id_boucle."&gt;("
+		. $type . ")</blink><br>\n"
+		. "<b>"._T('avis_erreur_mysql')."</b><br>\n"
+		. htmlspecialchars($query)
+		. "<br><font color='red'><b>".htmlspecialchars($erreur)
+		. "</b></font><br>"
+		. "<blink>&lt;/BOUCLE".$id_boucle."&gt;</blink></tt>\n";
+
+		include_ecrire('inc_lang.php3');
+		utiliser_langue_visiteur();
+		$retour .= aide('erreur_mysql');
+		spip_log("Erreur MySQL BOUCLE$id_boucle (".$GLOBALS['fond'].".html)");
+	}
+
+	// Pour un visiteur normal, afficher juste le fait qu'il y a une erreur
+	// ajouter &afficher_erreurs=1 pour discuter sur spip@rezo.net
+	if (!$HTTP_COOKIE_VARS['spip_admin'] AND !$auteur_session
+	AND !$GLOBALS['afficher_erreurs'])
+		return "<br />\n<b>"._T('info_erreur_squelette')."</b><br />\n";
+	else
+		return "<div style='position: fixed; top: 10px; left: 10px;
+		z-index: 10000; background-color: pink;'>$retour</div>";
+}
+
+
+//
+// Erreur au parsing des squelettes : afficher le code fautif
+//
+function erreur_squelette($message, $fautif, $lieu) {
+	global $auteur_session;
+
+	// Drapeau pour interdire d'ecrire les fichiers dans le cache
+	define('spip_erreur_fatale', 'erreur_squelette');
+
+	spip_log("Erreur squelette: $message | $fautif $lieu ("
+	.$GLOBALS['fond'].".html)");
+	$GLOBALS['bouton_admin_debug'] = true;
+
+	// Pour un visiteur normal, ne rien afficher, si SPIP peut s'en sortir
+	// tant mieux, sinon l'erreur se verra de toutes facons :-(
+	// ajouter &afficher_erreurs=1 pour discuter sur spip@rezo.net
+	if ($HTTP_COOKIE_VARS['spip_admin'] OR $auteur_session
+	OR $GLOBALS['afficher_erreurs']) {
+		$message = "<h2>"._T('info_erreur_squelette')."</h2><p>$message</p>";
+		if ($fautif)
+			$message .= ' (<FONT color="#FF000">'
+			. entites_html($fautif) . '</FONT>)';
+		$message .= '<br /><FONT color="#FF000">' . $lieu . '</FONT>'; 
+
+		echo "<div style='position: fixed; top: 10px; left: 10px;
+		z-index: 10000; background-color: pink;'>$message</div>";
+	}
+}
+
 ?>
diff --git a/inc-arg-squel.php3 b/inc-arg-squel.php3
index 0d345de6d2..a4fc502e95 100644
--- a/inc-arg-squel.php3
+++ b/inc-arg-squel.php3
@@ -98,8 +98,9 @@ function calculer_params($idb, &$boucles) {
 				if ($boucle->order) {
 					$boucle->order .= ' DESC';
 				} else {
-				  return array(_T('info_erreur_squelette'),
-					       $idb . (_L("&nbsp: inversion d'un ordre inexistant")));
+					include_local('inc-admin.php3');
+					erreur_squelette(_T('info_erreur_squelette'),
+					_L("&nbsp: inversion d'un ordre inexistant"), $idb);
 				}
 			}
 
@@ -140,10 +141,9 @@ function calculer_params($idb, &$boucles) {
 				$col = $match[1];
 				$col_table = $id_table;
 				// Valeur de comparaison
-				if ($match[3]) {
+				if ($match[3])
 					$val = calculer_param_dynamique($match[6], $boucles, $idb);
-					if (is_array($val)) return $val; #erreur
-				} else {
+				else {
 					$val = $match[1];
 					// Si id_parent, comparer l'id_parent avec l'id_objet
 					// de la boucle superieure
@@ -419,8 +419,9 @@ function calculer_param_dynamique($val, &$boucles, $idb) {
 		if (ereg("[$]Pile[[][^]]+[]][[]'[^]]*'[]]", $c, $v))
 			return $v[0];
 		else {
-			spip_log("champ inexistant ? : $c");
-			return $c;
+			# erreur
+			include_local("inc-admin.php3");
+			erreur_squelette(_L("parametre dynamique inexistant ?"), '', $idb);
 		}
 	} else {
 		if (ereg('^\$(.*)$',$val,$m))
diff --git a/inc-cache.php3 b/inc-cache.php3
index 8e6bf86f68..d5ef5409c6 100644
--- a/inc-cache.php3
+++ b/inc-cache.php3
@@ -18,7 +18,11 @@ function nettoyer_uri() {
 	return $fichier_requete;
 }
 
-// le format souhaite : "CACHE/a/8400/bout-d-url.md5(.gz)"
+//
+// Le format souhaite : "CACHE/a/(8400/)bout-d-url.md5(.gz)"
+// Attention a modifier simultanement le sanity check de
+// la fonction retire_cache() dans ecrire/inc_invalideur.php3
+//
 function generer_nom_fichier_cache($contexte='', $fond='') {
 	global $delais;
 	global $flag_gz, $compresser_cache;
diff --git a/inc-calcul-squel.php3 b/inc-calcul-squel.php3
index 8261dca435..804fa93384 100644
--- a/inc-calcul-squel.php3
+++ b/inc-calcul-squel.php3
@@ -358,18 +358,18 @@ function calculer_liste($tableau, $prefix, $id_boucle, $niv, &$boucles, $id_mere
 			$rendu[0][0] = "/"."* $commentaire *"."/ ".$rendu[0][0];
 
 		//
-		// (_f(0,principal) ? avant._f(1).apres : _f(-1).alternatif)
+		// (_f(1,principal) ? avant._f().apres : _f().alternatif)
 		// _f() fonctionne avec une pile ; cette structure logique permet
 		// de n'evaluer que ce qui doit l'etre (ne pas evaluer avant/apres
 		// si on veut utiliser sinon, et vice-versa)
 		if ($utiliser_f)
-			$code = "(_f(0,".$rendu[0][0].") ? "
+			$code = "(_f(1,".$rendu[0][0].") ? "
 			. (($rendu[1][0]=="''") ? "" :
 				"\n$tab\t/"."* << *"."/".$rendu[1][0]." .")
-			. "_f(1)"
+			. "_f()"
 			. (($rendu[2][0]=="''") ? "" :
 				". ".$rendu[2][0]."/"."* >> *"."/")
-			. " : _f(-1)"
+			. " : _f()"
 			. (($rendu[3][0]=="''") ? "" :
 				" /"."* sinon: *"."/.".$rendu[3][0])
 			.")";
@@ -423,30 +423,25 @@ function calculer_squelette($squelette, $nom, $gram, $sourcefile) {
 		if ($boucle->type_requete == 'boucle') {
 			$rec = &$boucles[$boucle->param];
 			if (!$rec) {
-				return array(_T('info_erreur_squelette'),
-				($boucle->param . _L('&nbsp: boucle recursive non definie')));
-			} 
-
-			$rec->externe = $id;
-			$boucles[$id]->return =
-				calculer_liste(array($rec),
-					$nom,
-					$boucle->param,
-					1,
-					$boucles,
-					$id);
+				include_local('inc-admin.php3');
+				erreur_squelette(_T('info_erreur_squelette'),
+				_L($boucle->param.'&nbsp: boucle recursive non definie'), $id);
+			} else {
+				$rec->externe = $id;
+				$boucles[$id]->return =
+					calculer_liste(array($rec),
+						$nom,
+						$boucle->param,
+						1,
+						$boucles,
+						$id);
+			}
 		}
 	} 
 
 	if ($boucles) foreach($boucles as $id => $boucle) { 
 		if ($boucle->type_requete != 'boucle') {
 			$res = calculer_params($id, $boucles);
-
-			// C'est quoi ca : remettre la gestion d'erreur
-			// au niveau de l'erreur (cf. inc-arg-squel)
-			if (is_array($res))
-				return $res;
-
 			$boucles[$id]->return = calculer_liste($boucle->milieu,
 				$nom,
 				$id,
@@ -457,7 +452,7 @@ function calculer_squelette($squelette, $nom, $gram, $sourcefile) {
 	}
 
 	// idem pour la racine
-	list($return,$corps) = calculer_liste($racine, $nom, '',0, $boucles, '');
+	list ($return,$corps) = calculer_liste($racine, $nom, '',0, $boucles, '');
 
 
 	// Corps de toutes les fonctions PHP,
diff --git a/inc-calcul.php3 b/inc-calcul.php3
index 0e8dbbae33..3040457390 100644
--- a/inc-calcul.php3
+++ b/inc-calcul.php3
@@ -87,8 +87,7 @@ function charger_squelette ($squelette) {
 		eval('?'.'>'.$skel_compile);
 
 		if (function_exists($nom)) {
-			if (!$GLOBALS['var_preview'])
-				ecrire_fichier ($phpfile, $skel_compile);
+			ecrire_fichier ($phpfile, $skel_compile);
 			return $nom;
 		}
 		else {
@@ -296,9 +295,8 @@ function calculer_page($chemin_cache, $elements, $delais, $inclusion=false) {
 	serialize($page['signal']))." -->\n";
 
 	// Enregistrer le fichier cache
-	if ($delais>0)
-		if (!$GLOBALS['var_preview'])
-			ecrire_fichier($chemin_cache, $signal.$page['texte']);
+	if ($delais > 0)
+		ecrire_fichier($chemin_cache, $signal.$page['texte']);
 
 	return $page;
 }
@@ -368,24 +366,21 @@ spip_log("cherche logo $type $id_objet $on $off $flag_fichier");
 
 
 // Fonction appelee par le skel pour assembler les balises
-function _f($action, $texte='') {
+function _f($push = false, $texte='') {
 	static $pile_f = array();
-	switch ($action) {
-		// push
-		case 0:
-			array_push($pile_f, $texte);
-			return ($texte <> '');
-			break;
-		// pop
-		case 1:
-			return array_pop($pile_f);
-			break;
-		// pop & ignore
-		case -1:
-			array_pop($pile_f);
-			return false;
-			break;
+
+	// push et teste si nul
+	if ($push) {
+		array_push($pile_f, $texte);
+		if ($texte)
+			return true;
 	}
+	// ou pop
+	// Attention "return array_pop($pile_f)" provoquerait
+	// l'affichage du chiffre zero pour, par exemple, #TOTAL_BOUCLE
+	else
+		if ($texte = array_pop($pile_f))
+			return $texte;
 }
 
 ?>
diff --git a/inc-calcul_html4.php b/inc-calcul_html4.php
index 26df7cc68f..ee1db983c9 100644
--- a/inc-calcul_html4.php
+++ b/inc-calcul_html4.php
@@ -104,50 +104,4 @@ function calcul_form_rech($lien)
     "<form action='$lien' method='get' class='formrecherche'><input type='text' id='formulaire_recherche' size='20' class='formrecherche' name='recherche' value='" . _T('info_rechercher') . "' /></form>";
 }
 
-# fonctions pour la balise FORMULAIRE_FORUM
-# $Cache est passe' par re'fe'rence pour e'viter sa recopie mais n'est pas affecte'
-
-function boutons_de_forum($idr, $idf, $ida, $idb, $ids, $titre, $table, $forum)
-{
-  $r = boutons_de_forum_table($idr, $idf, $ida, $idb, $ids, $titre, $table, $forum);
-  if (!$r) return '';
-  list($titre, $table, $forum) = $r;
-  $titre = ('> ' . supprimer_numero(ereg_replace ('^[>[:space:]]*', '',$titre)));
-  $url = $GLOBALS[HTTP_GET_VARS][url];
-  if (!$url) 
-# Y a intéret à avoir fait un rawurlencode sur tous les paramètres...
-    $url =  substr($GLOBALS['REQUEST_URI'],
-		   strrpos($GLOBALS['REQUEST_URI'], '/') + 1);
-
-  $retour_forum = $GLOBALS['retour'];
-  if (!$retour_forum)
-    $retour_forum = rawurlencode($url);
-  else $retour_forum = ereg_replace('&recalcul=oui','',$retour_forum);
-	$retour_forum = quote_amp($retour_forum);
-
-  $lacible = "
-	include_local('inc-forum.php3');
-	lang_select(\$GLOBALS['spip_lang']);
-	echo retour_forum('$idr','$idf','$ida','$idb','$ids','".texte_script($titre)."','$table', '$forum', '$url', \"
-	<input type='hidden' name='retour' value='$retour_forum' />
-	<input type='hidden' name='ajout_forum' value='oui' />
-	<input type='hidden' name='forum_id_rubrique' value='$idr' />
-	<input type='hidden' name='forum_id_parent' value='$idf' />
-	<input type='hidden' name='forum_id_article' value='$ida' />
-	<input type='hidden' name='forum_id_breve' value='$idb' />
-	<input type='hidden' name='forum_id_syndic' value='$ids' />
-	" .
-	(($forum != 'pri') ? '' : ((_T('forum_info_modere'). '<p>'))) . 
-	"\");
-	lang_dselect();";
-  return
-	 (($forum != "abo") ? ("<" . "?php" . $lacible . "?" . ">"):
-	   ("<" . "?php " . "if (\$GLOBALS[\"auteur_session\"]) {
-		$lacible}" . 'else
-		{
-		include_local("inc-login.php3"); 
-		login(new Link("' . $url . '"), false, true); }' . 
-	    "?". ">"));
-}
-
 ?>
diff --git a/inc-calcul_mysql3.php b/inc-calcul_mysql3.php
index 2fd021cdbf..ed83467e5f 100644
--- a/inc-calcul_mysql3.php
+++ b/inc-calcul_mysql3.php
@@ -20,90 +20,44 @@
 # - le nom de la table
 # - le nom de la boucle (pour le message d'erreur e'ventuel)
 
-# En commentaire, le court-circuit de spip_query,
-# avec traitement de table_prefix sans restriction sur son nom
-
-function spip_abstract_select($s, $f, $w='', $g='', $o='', $l='', $sous='', $cpt='', $table='', $id='') {
-# if ($GLOBALS["mysql_rappel_connexion"] AND $DB = $GLOBALS["spip_mysql_db"])
-#        $DB = "`$DB`";
-# $DB .= $GLOBALS["table_prefix"] . '_';
-  $DB = 'spip_';
- $q = "\nFROM\t$DB".
-    join(",\n\t$DB", $f) .
-    ((!$w) ? "" : ("\nWHERE\t".join("\n AND\t", $w))) .
-    ($g ? "\nGROUP BY $g" : '') .
-    ($o ? "\nORDER BY $o" : '') .
-    ($l ? "\nLIMIT $l" : '');
-  $q = (!$sous ? 
-	("\nSELECT\t". join(",\n\t", $s) . $q) :
-	("\nSELECT\tS_" . join(",\n\tS_", $s) .
-	 "\nFROM\t(" . join(",\n\t", $s) . " ,\n\tCOUNT(" . $sous .
-	 ") AS compteur $q)\n AS S_$table\nWHERE compteur= " . 
-	 $cpt));
-# spip_log("$id: $q\n");
-# if (!($result = @mysql_query($q)))
-  if (!($result = @spip_query($q)))
-    {
-      include_local("inc-debug-squel.php3");
-      echo erreur_requete_boucle($q, $id, $table);
-      exit;
-    }
-#  spip_log(spip_num_rows($result));
-  return $result;
+## NB : le traitement des SQL de forums est defini dans inc-forum.php3
+
+function spip_abstract_select (
+	$select = array(), $from = array(), $where = '',
+	$groupby = '', $orderby = '', $limit = '',
+	$sousrequete = '', $cpt = '',
+	$table = '', $id = '') {
+
+	$DB = 'spip_';
+	$q = " FROM $DB" . join(", $DB", $from)
+	. (is_array($where) ? ' WHERE ' . join(' AND ', $where) : '')
+	. ($groupby ? " GROUP BY $groupby" : '')
+	. ($orderby ? "\nORDER BY $orderby" : '')
+	. ($limit ? "\nLIMIT $limit" : '');
+
+	if (!$sousrequete)
+		$q = " SELECT ". join(", ", $select) . $q;
+	else
+		$q = " SELECT S_" . join(", S_", $select)
+		. " FROM (" . join(", ", $select)
+		. ", COUNT(".$sousrequete.") AS compteur " . $q
+		.") AS S_$table WHERE compteur=" . $cpt;
+
+	//
+	// Erreur ? C'est du debug, ou une erreur du serveur
+	//
+	if (!($result = @spip_query($q))) {
+		include_local('inc-admin.php3');
+		echo erreur_requete_boucle($q, $id, $table);
+	}
+
+	#  spip_log(spip_num_rows($result));
+	return $result;
 }
 
+
 # toutes les fonctions avec requete SQL, necessaires aux squelettes.
 
-function boutons_de_forum_table($idr, $idf, $ida, $idb, $ids, $titre, $table, $forum)
-{
-  if (($table == 'forums') || !$table)
-    {
-      $forum = spip_fetch_array(spip_query("
-SELECT	accepter_forum
-FROM	spip_articles
-WHERE	id_article='" . ($ida ? $ida : substr(lire_meta("forums_publics"),0,3)) . "'
-")); 
-      $forum = ($forum ? $forum['accepter_forum'] : substr(lire_meta("forums_publics"),0,3));
-    }
-  if ($forum=="non") return '';
-  // si FORMULAIRE_FORUM a e'te' employe' hors d'une boucle,
-  // on n'a pas pu de'terminer titre et table a` la compil
-  if (!$table)
-    {
-      if ($idf)
-	{
-	  $r = "SELECT titre FROM spip_forum WHERE id_forum = $idf";
-	  $table = "forum";
-	}
-      else if ($idr)
-	{
-	  $r = "SELECT titre FROM spip_rubriques WHERE id_rubrique = $idr";
-	  $table = "rubriques";
-	}
-      else if ($ida)
-	{
-	  $r = "SELECT titre FROM spip_articles WHERE id_article = $ida";
-	  $table = "articles";
-	}
-      else if ($idb)
-	{
-	  $r = "SELECT titre FROM spip_breves WHERE id_breve = $idb";
-	  $table = "breves";
-	}
-      else if ($ids)
-	{
-	  $table = "syndic";
-	  $r = "SELECT nom_site AS titre FROM spip_syndic WHERE id_syndic = $ids";
-	}
-      else
-	{
-	  $r = "SELECT '".addslashes(_T('forum_titre_erreur'))."' AS titre";
-	}
-      $r = spip_fetch_array(spip_query($r));
-      $titre = $r['titre'];
-    }
-  return array($titre, $table, $forum);
-}
 
 function calcul_exposer ($id, $type, $reference) {
 	static $exposer;
@@ -236,44 +190,46 @@ WHERE	id_article='$id_article' AND statut='publie'
 }
 
 // Calcul de la rubrique associee a la requete
-// (selection de squelette specifique)
+// (selection de squelette specifique par id_rubrique & lang)
 
-function sql_rubrique_fond($contexte, $lang)
-{
-  if ($id = $contexte['id_rubrique']) {
-    if ($row = spip_fetch_array(spip_query("SELECT lang FROM spip_rubriques WHERE id_rubrique='$id'")))
-      if ($row['lang']) $lang = $row['lang'];
-    return array($id, $lang);
-  }
-  if ($id  = $contexte['id_breve']) {
-    if ($row = spip_fetch_array(spip_query("
-SELECT id_rubrique FROM spip_breves WHERE id_breve='$id'"))) {
-      $id_rubrique_fond = $row['id_rubrique'];
-      if ($row = spip_fetch_array(spip_query("
-SELECT lang FROM spip_rubriques WHERE id_rubrique='$id_rubrique_fond'")))
-	if ($row['lang']) $lang = $row['lang'];
-    }
-    return array($id_rubrique_fond, $lang);
-  }
-  if ($id = $contexte['id_syndic']) {
-    if ($row = spip_fetch_array(spip_query("
-SELECT id_rubrique FROM spip_syndic WHERE id_syndic='$id'"))) {
-      $id_rubrique_fond = $row['id_rubrique'];
-      if ($row = spip_fetch_array(spip_query("
-SELECT lang FROM spip_rubriques WHERE id_rubrique='$id_rubrique_fond'")))
-	if ($row['lang']) $lang = $row['lang'];
-    }
-    return array($id_rubrique_fond, $lang);
-  }
-  if ($id = $contexte['id_article']) {
-    if ($row = spip_fetch_array(spip_query("
-SELECT id_rubrique,lang FROM spip_articles WHERE id_article='$id'"))) {
-      $id_rubrique_fond = $row['id_rubrique'];
-      if ($row['lang']) $lang = $row['lang'];
-    }
-    return array($id_rubrique_fond, $lang);
-  }
-  return '';
+function sql_rubrique_fond($contexte, $lang) {
+
+	if ($id = intval($contexte['id_rubrique'])) {
+		$row = spip_fetch_array(spip_query(
+		"SELECT lang FROM spip_rubriques WHERE id_rubrique='$id'"));
+		if ($row['lang'])
+			$lang = $row['lang'];
+		return array ($id, $lang);
+	}
+
+	if ($id  = intval($contexte['id_breve'])) {
+		$row = spip_fetch_array(spip_query(
+		"SELECT id_rubrique, lang FROM spip_breves WHERE id_breve='$id'"));
+		$id_rubrique_fond = $row['id_rubrique'];
+		if ($row['lang'])
+			$lang = $row['lang'];
+		return array($id_rubrique_fond, $lang);
+	}
+
+	if ($id = intval($contexte['id_syndic'])) {
+		$row = spip_fetch_array(spip_query("SELECT id_rubrique
+		FROM spip_syndic WHERE id_syndic='$id'"));
+		$id_rubrique_fond = $row['id_rubrique'];
+		$row = spip_fetch_array(spip_query("SELECT lang
+		FROM spip_rubriques WHERE id_rubrique='$id_rubrique_fond'"));
+		if ($row['lang'])
+			$lang = $row['lang'];
+		return array($id_rubrique_fond, $lang);
+	}
+
+	if ($id = intval($contexte['id_article'])) {
+		$row = spip_fetch_array(spip_query("SELECT id_rubrique,lang
+		FROM spip_articles WHERE id_article='$id'"));
+		$id_rubrique_fond = $row['id_rubrique'];
+		if ($row['lang'])
+			$lang = $row['lang'];
+		return array($id_rubrique_fond, $lang);
+	}
 }
 
 
diff --git a/inc-debug-squel.php3 b/inc-debug-squel.php3
deleted file mode 100755
index b810e42522..0000000000
--- a/inc-debug-squel.php3
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-//
-// Ce fichier ne sera execute qu'une fois
-if (defined("_INC_DEBUG_SKEL")) return;
-define("_INC_DEBUG_SKEL", "1");
-include_ecrire("inc_presentation.php3");
-
-function erreur_requete_boucle($query, $id_boucle, $type) {
-	$GLOBALS["delais"]=0;
-	$erreur = spip_sql_error();
-	$errno = spip_sql_errno();
-	if (eregi('err(no|code):?[[:space:]]*([0-9]+)', $erreur, $regs))
-		$errsys = $regs[2];
-	else if (($errno == 1030 OR $errno <= 1026) AND ereg('[^[:alnum:]]([0-9]+)[^[:alnum:]]', $erreur, $regs))
-		$errsys = $regs[1];
-
-	$erreur = htmlspecialchars($erreur);
-
-	// Erreur systeme
-	if ($errsys > 0 AND $errsys < 200) {
-		$retour .= "<tt><br><br><blink>"._T('info_erreur_systeme', array('errsys'=>$errsys))."</blink><br>\n";
-		if ($GLOBALS['spip_admin'])
-		$retour .= 
-			_T('info_erreur_systeme2').
-				"<blink>"._T('info_erreur_systeme', array('errsys'=>$errsys))."</blink>";
-	}
-	// Requete erronee
-	else {
-		$retour .= "<tt><blink>&lt;BOUCLE".
-		  $id_boucle.
-		  "&gt;(".
-		  $type .
-		  ")</blink><br>\n".
-		"<b>"._T('avis_erreur_mysql')."</b><br>\n".
-		  htmlspecialchars($query)."<br><font color='red'><b>$erreur</b></font><br>".
-		  "<blink>&lt;/BOUCLE".$id_boucle."&gt;</blink></tt>\n";
-		if ($GLOBALS['spip_admin']) {
-		  include_ecrire('inc_lang.php3');
-		  utiliser_langue_visiteur();
-		  $retour .= aide('erreur_mysql');
-		}
-	}
-	return "<div style='position: fixed; top: 10px; left: 10px; z-index: 10000'>$retour</div>";
-}
-
-function erreur_squelette($message, $fautif, $lieu)
-{
-  install_debut_html($message);
-  if ($fautif)
-    echo ' (<FONT color="#FF000">' . entites_html($fautif) . '</FONT>)';
-  echo '<br /><FONT color="#FF000">' . $lieu . '</FONT>.'; 
-  install_fin_html();
-  exit;
-}
-?>
diff --git a/inc-form-squel.php3 b/inc-form-squel.php3
index 1eaddb4575..5614ffd5c7 100644
--- a/inc-form-squel.php3
+++ b/inc-form-squel.php3
@@ -76,119 +76,20 @@ function balise_FORMULAIRE_SITE_dist($p) {
 	return $p;
 }
 
-
+//
+// Formulaires de gestion de forums : les balises sont definies
+// dans le fichier inc-forum.php3 qui centralise toute la gestion des forums
+//
 // Formulaire de reponse a un forum
-function balise_FORMULAIRE_FORUM_dist($p) {
-	$type = $p->type_requete;
-	switch ($type) {
-		case 'breves':
-			$p->code = "boutons_de_forum('', '', ''," .
-				champ_sql('id_breve', $p) .
-				", '', " .
-				champ_sql('titre', $p) .
-				", '$type', substr(lire_meta('forums_publics'),0,3)), \$Cache)";
-		break;
-
-		case 'rubriques':
-			$p->code = 'boutons_de_forum(' .
-			champ_sql('id_rubrique', $p) .
-			", '', '', '', ''," .
-			champ_sql('titre', $p) .
-			", '$type', substr(lire_meta('forums_publics'),0,3)), \$Cache)";
-			break;
-
-		case 'syndication':
-			$p->code = "boutons_de_forum('', '', '','', " .
-			champ_sql('id_rubrique', $p) . ", " .
-			champ_sql('nom_site', $p) .
-			", '$type', substr(lire_meta('forums_publics'),0,3)), \$Cache)";
-			break;
-    
-		case 'articles': 
-			$p->code = "boutons_de_forum('', '', " .
-			champ_sql('id_article', $p) .
-			", '','', " .
-			champ_sql('nom_site', $p) .
-			"'$type', " .
-			champ_sql('accepter_forum', $p) .
-			', $Cache)';
-			break;
-
-		case 'forums':
-		default:
-			$p->code = "boutons_de_forum(" .
-			champ_sql('id_rubrique', $p) . ', ' .
-			champ_sql('id_forum', $p) . ', ' .
-			champ_sql('id_article', $p) . ', ' .
-			champ_sql('id_breve', $p) . ', ' .
-			champ_sql('id_syndic', $p) . ', ' .
-			champ_sql('titre', $p) .
-			", '$type', '', \$Cache)";
-			break;
-	}
-
-	$p->type = 'php';
-	return $p;
-}
-
+#	function balise_FORMULAIRE_FORUM_dist($p) {}
 // Parametres de reponse a un forum
-function balise_PARAMETRES_FORUM_dist($p) {
-	$_accepter_forum = champ_sql('accepter_forum', $p);
-	$p->code = '
-	// refus des forums ?
-	('.$_accepter_forum.'=="non" OR
-	(lire_meta("forums_publics") == "non" AND '.$_accepter_forum.'!="oui"))
-	? "" : // sinon:
-	';
-
-
-	switch ($p->type_requete) {
-		case 'articles':
-			$c = '"id_article=".' . champ_sql('id_article', $p);
-			break;
-		case 'breves':
-			$c = '"id_breve=".' . champ_sql('id_breve', $p);
-			break;
-		case 'rubriques':
-			$c = '"id_rubrique=".' . champ_sql('id_rubrique', $p);
-			break;
-		case 'syndication':
-			$c = '"id_syndic=".' . champ_sql('id_syndic', $p);
-			break;
-		case 'forums':
-		default:
-			$liste_champs = array ("id_article","id_breve","id_rubrique","id_syndic","id_forum");
-			foreach ($liste_champs as $champ) {
-				$x = champ_sql( $champ, $p);
-				$c .= (($c) ? ".\n" : "") . "((!$x) ? '' : ('&$champ='.$x))";
-			}
-			$c = "substr($c,1)";
-			break;
-	}
+#	function balise_PARAMETRES_FORUM_dist($p) {}
+include_local('inc-forum.php3');
 
-	$c .= '.
-	"&retour=".rawurlencode($lien=$GLOBALS["HTTP_GET_VARS"]["retour"] ? $lien : nettoyer_uri())';
-
-	// Noter l'invalideur de la page contenant ces parametres,
-	// en cas de premier post sur le forum (a mettre dans ecrire/inc_forum et
-	// a repliquer sur les autres balises FORUM)
-	$invalide = '
-	// invalideur forums
-	(!($Cache[\'id_forum\'][calcul_index_forum(' . 
-				// Retournera 4 [$SP] mais force la demande du champ a MySQL
-				champ_sql('id_article', $p) . ',' .
-				champ_sql('id_breve', $p) .  ',' .
-				champ_sql('id_rubrique', $p) .',' .
-				champ_sql('id_syndic', $p) .  ")]=1)?'':\n";
-	$p->code .= $invalide."(".$c."))";
-
-	$p->type = 'html';
-	return $p;
-}
 
-/*
-# Boutons d'administration: 
-*/
+//
+// Boutons d'administration: 
+//
 function balise_FORMULAIRE_ADMIN_dist($p) {
 	$p->code = "'<!-- @@formulaire_admin@@45609871@@ -->'";
 	$p->type = "php";
diff --git a/inc-forum.php3 b/inc-forum.php3
index 306e471cd9..c8deba1bea 100644
--- a/inc-forum.php3
+++ b/inc-forum.php3
@@ -21,14 +21,19 @@ else {
 	include_local ("inc-urls-dist.php3");
 }
 
-// fabrique un bouton d'attribut Name $n, d'attribut Value $v et autres $a
 
+/*******************************/
+/* GESTION DU FORMULAIRE FORUM */
+/*******************************/
+
+// fabrique un bouton d'attribut Name $n, d'attribut Value $v et autres $a
 function boutonne($a, $n, $v) {
      return "\n<input $a name='$n' value=\"$v\" />";
 }
 
-// Re'ponse a` un forum 
-
+//
+// Le code dynamique appelee par les squelettes
+//
 function retour_forum($id_rubrique, $id_parent, $id_article, $id_breve, $id_syndic, $titre, $table, $forums_publics, $url, $hidden) {
 
 	global $REMOTE_ADDR, $id_message, $afficher_texte, $spip_forum_user;
@@ -44,45 +49,30 @@ function retour_forum($id_rubrique, $id_parent, $id_article, $id_breve, $id_synd
 
 		if ($afficher_texte != 'non') {
 			$bouton = 
-			  "<div style='font-size: 120%; font-weigth: bold;'>" .
-			  typo($titre) .
-			  "</div><p /><b><a href='mailto:" .
-			  entites_html($email_auteur) .
-			  "'>" .
-			  typo($auteur) .
-			  "</a></b><p />" .
-			  propre($texte) .
-			  "<p /><a href='" .
-			  entites_html($url_site) .
-			  "'>" .
-			  typo($nom_site_forum) .
-			  "</a>";
+			"<div style='font-size: 120%; font-weigth: bold;'>"
+			. typo($titre)
+			. "</div><p /><b><a href='mailto:"
+			. entites_html($email_auteur) . "'>"
+			. typo($auteur) . "</a></b><p />"
+			. propre($texte) . "<p /><a href='"
+			. entites_html($url_site) . "'>"
+			. typo($nom_site_forum) . "</a>";
 
 			// Verifier mots associes au message
-			$result_mots = spip_query("
-SELECT	mots.id_mot, mots.titre, mots.type
-FROM	spip_mots_forum AS lien,
-	spip_mots AS mots 
-WHERE	id_forum='$id_message'
-    AND mots.id_mot = lien.id_mot 
-GROUP BY mots.id_mot
-");
-			if (spip_num_rows($result_mots)>0) 
-			  {
-			    $bouton .= "<p>".
-			      _T('forum_avez_selectionne') .
-			      "</p><ul>";
-			    while ($row = spip_fetch_array($result_mots)) {
-			      $les_mots[$row['id_mot']] = "checked='checked'";
-			      $presence_mots = true;
-			      $bouton .= "<li class='font-size=80%'> ".
-				$row['type'] .
-				"&nbsp;: <b>" .
-				$row['titre'] . 
-				"</b></li>";
-			    }
-			    $bouton .= '</ul>';
-			  }
+			$result_mots = spip_query("SELECT mots.id_mot, mots.titre, mots.type
+			FROM spip_mots_forum AS lien, spip_mots AS mots 
+			WHERE id_forum='$id_message' AND mots.id_mot = lien.id_mot
+			GROUP BY mots.id_mot");
+			if (spip_num_rows($result_mots)>0) {
+				$bouton .= "<p>"._T('forum_avez_selectionne')."</p><ul>";
+				while ($row = spip_fetch_array($result_mots)) {
+					$les_mots[$row['id_mot']] = "checked='checked'";
+					$presence_mots = true;
+					$bouton .= "<li class='font-size=80%'> "
+					. $row['type'] . "&nbsp;: <b>" . $row['titre'] ."</b></li>";
+				}
+				$bouton .= '</ul>';
+			}
 
 			if (strlen($texte) < 10 AND !$presence_mots) {
 				$bouton .= "<p align='right' style='color: red;'>"._T('forum_attention_dix_caracteres')."</p>\n";
@@ -97,7 +87,7 @@ GROUP BY mots.id_mot
 		}
 	} else {
 		// Si premiere edition, initialiser l'auteur
-	  	// puis s'accorder une nouvelle entre'e dans la table
+	  	// puis s'accorder une nouvelle entree dans la table
 		if ($spip_forum_user && is_array($cookie_user = unserialize($spip_forum_user))) {
 			$auteur = $cookie_user['nom'];
 			$email_auteur = $cookie_user['email'];
@@ -106,39 +96,39 @@ GROUP BY mots.id_mot
 			$auteur = $GLOBALS['auteur_session']['nom'];
 			$email_auteur = $GLOBALS['auteur_session']['email'];
 		}
-		spip_query("
-INSERT INTO spip_forum (date_heure, titre, ip, statut)
-VALUES (NOW(), \"".addslashes($titre)."\", \"$REMOTE_ADDR\", \"redac\")
-");
+		spip_query("INSERT INTO spip_forum (date_heure, titre, ip, statut)
+		VALUES (NOW(), '".addslashes($titre)."', '$REMOTE_ADDR', 'redac')");
 		$id_message = spip_insert_id();
 	}
 
-	        // Generation d'une valeur de securite pour validation
-        $seed = (double) (microtime() + 1) * time() * 1000000;
-        @mt_srand($seed);
-        $alea = @mt_rand();
-        if (!$alea) {srand($seed);$alea = rand();}
-        $forum_id_rubrique = intval($id_rubrique);
-        $forum_id_parent = intval($id_parent);
-        $forum_id_article = intval($id_article);
-        $forum_id_breve = intval($id_breve);
-        $forum_id_syndic = intval($id_syndic);
-        $hash = calculer_action_auteur("ajout_forum $forum_id_rubrique $forum_id_parent $forum_id_article $forum_id_breve $forum_id_syndic $alea");
+	// Generation d'une valeur de securite pour validation
+	$seed = (double) (microtime() + 1) * time() * 1000000;
+	@mt_srand($seed);
+	$alea = @mt_rand();
+	if (!$alea) {srand($seed);$alea = rand();}
+	$forum_id_rubrique = intval($id_rubrique);
+	$forum_id_parent = intval($id_parent);
+	$forum_id_article = intval($id_article);
+	$forum_id_breve = intval($id_breve);
+	$forum_id_syndic = intval($id_syndic);
+	$hash = calculer_action_auteur("ajout_forum $forum_id_rubrique $forum_id_parent $forum_id_article $forum_id_breve $forum_id_syndic $alea");
 	$titre = entites_html($titre);
 	if (!$url_site) $url_site = "http://";
 	if ($forums_publics == "abo") $disabled = " disabled='disabled'";
 
+	// Faut-il ajouter des propositions de mots-cles
 	if ((lire_meta("mots_cles_forums") == "oui") && ($table != 'forum'))
-	  $table= table_des_mots($table, $les_mots);
-	else $table = '';
-
+		$table = table_des_mots($table, $les_mots);
+	else
+		$table = '';
 
 	$url = quote_amp($url);
+
 	return ("<form action='$url' method='post' name='formulaire'>\n$hidden" .
 		boutonne("type='hidden'", 'id_message', $id_message) .
 		boutonne("type='hidden'", 'alea', $alea) .
-                boutonne("type='hidden'", 'hash', $hash) .
-  		(($afficher_texte == "non") ?
+		boutonne("type='hidden'", 'hash', $hash) .
+		(($afficher_texte == "non") ?
 		 (boutonne("type='hidden'", 'titre', $titre) .
 		  $table .
 		  "\n<br /><div align='right'>" .
@@ -196,6 +186,11 @@ VALUES (NOW(), \"".addslashes($titre)."\", \"$REMOTE_ADDR\", \"redac\")
 		  "</div>\n</form>")));
 }
 
+
+// Mots-cles dans les forums :
+// Si la variable de personnalisation $afficher_groupe[] est definie
+// dans le fichier d'appel, et si la table de reference est OK, proposer
+// la liste des mots-cles
 function table_des_mots($table, $les_mots) {
 	global $afficher_groupe;
 
@@ -205,58 +200,246 @@ function table_des_mots($table, $les_mots) {
 	$result_groupe = spip_query("SELECT * FROM spip_groupes_mots
 	WHERE 6forum = 'oui' AND $table = 'oui'". $in_group);
 
-  $ret = '';
-  while ($row_groupe = spip_fetch_array($result_groupe)) {
-    $id_groupe = $row_groupe['id_groupe'];
-    $titre_groupe = propre($row_groupe['titre']);
-    $unseul = ($row_groupe['unseul']== 'oui') ? 'radio' : 'checkbox';
-    $result =spip_query("SELECT * FROM spip_mots WHERE id_groupe='$id_groupe'");
-    $total_rows = spip_num_rows($result);
-    
-    if ($total_rows > 0){
-      $ret .= "\n<p /><div class='spip_encadrer' style='font-size: 80%;'>";
-      $ret.= "<b>$titre_groupe&nbsp;:</b>";
-      $ret .= "<table cellpadding='0' cellspacing='0' border='0' width='100%'>\n";
-      $ret .= "<tr><td width='47%' valign='top'>";
-      $i = 0;
+	$ret = '';
+	while ($row_groupe = spip_fetch_array($result_groupe)) {
+		$id_groupe = $row_groupe['id_groupe'];
+		$titre_groupe = propre($row_groupe['titre']);
+		$unseul = ($row_groupe['unseul']== 'oui') ? 'radio' : 'checkbox';
+		$result =spip_query("SELECT * FROM spip_mots
+		WHERE id_groupe='$id_groupe'");
+		$total_rows = spip_num_rows($result);
+
+		if ($total_rows > 0) {
+			$ret .= "\n<p />";
+			$ret .= "<div class='spip_encadrer' style='font-size: 80%;'>";
+			$ret.= "<b>$titre_groupe&nbsp;:</b>";
+			$ret .= "<table cellpadding='0' cellspacing='0' border='0' width='100%'>\n";
+			$ret .= "<tr><td width='47%' valign='top'>";
+			$i = 0;
       
-      while ($row = spip_fetch_array($result)) {
-	$id_mot = $row['id_mot'];
-	$titre_mot = propre($row['titre']);
-	$descriptif_mot = propre($row['descriptif']);
-	
-	if ($i >= ($total_rows/2) AND $i < $total_rows)
-	  {
-	    $i = $total_rows + 1;
-	    $ret .= "</td><td width='6%'>&nbsp;</td><td width='47%' valign='top'>";
+		while ($row = spip_fetch_array($result)) {
+			$id_mot = $row['id_mot'];
+			$titre_mot = propre($row['titre']);
+			$descriptif_mot = propre($row['descriptif']);
+
+			if ($i >= ($total_rows/2) AND $i < $total_rows) {
+				$i = $total_rows + 1;
+				$ret .= "</td><td width='6%'>&nbsp;</td>
+				<td width='47%' valign='top'>";
+			}
+
+			$ret .= boutonne("type='$unseul' id='mot$id_mot' "
+			. $les_mots[$id_mot], "ajouter_mot[$id_groupe][]", $id_mot)
+			. afficher_petits_logos_mots($id_mot)
+			. "<b><label for='mot$id_mot'>$titre_mot</label></b><br />";
+
+			if ($descriptif_mot)
+				$ret .= "$descriptif_mot<br />";
+			$i++;
+		}
+
+		$ret .= "</td></tr></table>";
+		$ret .= "</div>";
+		}
 	}
-	
-	$ret .= boutonne("type='$unseul' id='mot$id_mot' " . $les_mots[$id_mot],
-			 "ajouter_mot[$id_groupe][]", 
-			 $id_mot) .
-	  afficher_petits_logos_mots($id_mot) .
-	  "<b><label for='mot$id_mot'>$titre_mot</label></b><br />";
-	if (strlen($descriptif_mot) > 0) $ret .= "$descriptif_mot<br />";
-	$i++;
-      }
-      
-      $ret .= "</td></tr></table>";
-      $ret .= "</div>";
-    }
-  }
-  return $ret;
+
+	return $ret;
 }
 
+
 function afficher_petits_logos_mots($id_mot) {
-  $image = cherche_image_nommee("moton$id_mot");
-  if ($image) {
-    $taille = getimagesize($image);
-    $largeur = $taille[0];
-    $hauteur = $taille[1];
-    if ($largeur < 100 AND $hauteur < 100)
-      return "<img src='$image' align='middle' width='$largeur' height='$hauteur' hspace='1' vspace='1' alt=' ' border=0 class='spip_image'> ";
-  }
-  return "";
+	$image = cherche_image_nommee("moton$id_mot");
+	if ($image) {
+		$taille = @getimagesize($image);
+		$largeur = $taille[0];
+		$hauteur = $taille[1];
+		if ($largeur < 100 AND $hauteur < 100)
+			return "<img src='$image' align='middle' width='$largeur'
+			height='$hauteur' hspace='1' vspace='1' alt=' ' border='0'
+			class='spip_image' /> ";
+	}
+}
+
+
+
+/*******************************************/
+/* DEFINITION DES BALISES LIEES AUX FORUMS */
+/*******************************************/
+
+// Noter l'invalideur de la page contenant ces parametres,
+// en cas de premier post sur le forum
+function code_invalideur_forums($p, $code) {
+	return '
+	// invalideur forums
+	(!($Cache[\'id_forum\'][calcul_index_forum(' . 
+				// Retournera 4 [$SP] mais force la demande du champ a MySQL
+				champ_sql('id_article', $p) . ',' .
+				champ_sql('id_breve', $p) .  ',' .
+				champ_sql('id_rubrique', $p) .',' .
+				champ_sql('id_syndic', $p) .  ")]=1)".
+				"?'':\n" . $code .")";
+}
+
+
+// Formulaire de reponse a un forum
+function balise_FORMULAIRE_FORUM_dist($p) {
+	$code = "code_de_forum_spip(" .
+	champ_sql('id_rubrique', $p) . ', ' .
+	champ_sql('id_forum', $p) . ', ' .
+	champ_sql('id_article', $p) . ', ' .
+	champ_sql('id_breve', $p) . ', ' .
+	champ_sql('id_syndic', $p) . ')';
+
+	$p->code = code_invalideur_forums($p, "(".$code.")");
+
+	$p->type = 'php';
+	return $p;
+}
+
+
+// Parametres de reponse a un forum
+function balise_PARAMETRES_FORUM_dist($p) {
+	$_accepter_forum = champ_sql('accepter_forum', $p);
+	$p->code = '
+	// refus des forums ?
+	('.$_accepter_forum.'=="non" OR
+	(lire_meta("forums_publics") == "non" AND '.$_accepter_forum.'!="oui"))
+	? "" : // sinon:
+	';
+
+	switch ($p->type_requete) {
+		case 'articles':
+			$c = '"id_article=".' . champ_sql('id_article', $p);
+			break;
+		case 'breves':
+			$c = '"id_breve=".' . champ_sql('id_breve', $p);
+			break;
+		case 'rubriques':
+			$c = '"id_rubrique=".' . champ_sql('id_rubrique', $p);
+			break;
+		case 'syndication':
+			$c = '"id_syndic=".' . champ_sql('id_syndic', $p);
+			break;
+		case 'forums':
+		default:
+			$liste_champs = array ("id_article","id_breve","id_rubrique","id_syndic","id_forum");
+			foreach ($liste_champs as $champ) {
+				$x = champ_sql( $champ, $p);
+				$c .= (($c) ? ".\n" : "") . "((!$x) ? '' : ('&$champ='.$x))";
+			}
+			$c = "substr($c,1)";
+			break;
+	}
+
+	$c .= '.
+	"&retour=".rawurlencode($lien=$GLOBALS["HTTP_GET_VARS"]["retour"] ? $lien : nettoyer_uri())';
+
+	$p->code = code_invalideur_forums($p, "(".$c.")");
+
+	$p->type = 'html';
+	return $p;
+}
+
+
+/*******************************************************/
+/* FONCTIONS DE CALCUL DES DONNEES DU FORMULAIRE FORUM */
+/*******************************************************/
+function code_de_forum_spip ($idr, $idf, $ida, $idb, $ids) {
+
+	// recuperer les donnees du forum auquel on repond, false = forum interdit
+	if (!$r = sql_recherche_donnees_forum ($idr, $idf, $ida, $idb, $ids))
+		return false;
+
+	list($titre, $table, $accepter_forum) = $r;
+
+	// titre propose pour la reponse
+	$titre = '> '.supprimer_numero(ereg_replace('^[>[:space:]]*', '',$titre));
+
+	// url de reference
+	if (!$url = $GLOBALS['HTTP_GET_VARS']['url']) 
+		$url = $GLOBALS['clean_link']->geturl();
+
+	// url de retour du forum
+	$retour_forum = $GLOBALS['retour'];
+	if (!$retour_forum)
+		$retour_forum = rawurlencode($url);
+	else $retour_forum = ereg_replace('&recalcul=oui','',$retour_forum);
+		$retour_forum = quote_amp($retour_forum);
+
+	// debut formulaire forum
+	$lacible = "
+	include_local('inc-forum.php3');
+	lang_select(\$GLOBALS['spip_lang']);
+	echo retour_forum('$idr','$idf','$ida','$idb','$ids','".texte_script($titre)."',
+	'".$table."', '".$accepter_forum."', '".$url."', \"
+	<input type='hidden' name='retour' value='".$retour_forum."' />
+	<input type='hidden' name='ajout_forum' value='oui' />
+	";
+
+	// identifiants des parents
+	if($idr) $lacible .= "<input type='hidden' name='forum_id_rubrique' value='$idr' />\n";
+	if($idf) $lacible .= "<input type='hidden' name='forum_id_parent' value='$idf' />\n";
+	if($ida) $lacible .= "<input type='hidden' name='forum_id_article' value='$ida' />\n";
+	if($idb) $lacible .= "<input type='hidden' name='forum_id_breve' value='$idb' />\n";
+	if($ids) $lacible .= "<input type='hidden' name='forum_id_syndic' value='$ids' />\n";
+
+	// message de moderation
+	$lacible .= (($accepter_forum != 'pri') ? '' :
+	((_T('forum_info_modere'). '<p>'))) . "\"); lang_dselect();";
+
+	// verifier l'identite des posteurs pour les forums sur abo
+	if ($accepter_forum == "abo")
+		$lacible = "
+		if (\$GLOBALS[\"auteur_session\"]) {\n".$lacible.'
+		} else {
+			include_local("inc-login.php3"); 
+			login(new Link("' . $url . '"), false, true);
+		}';
+
+	return "<" . "?php" . $lacible . "?" . ">";
+}
+
+//
+// Chercher le titre et la configuration du forum de l'element auquel on repond
+//
+function sql_recherche_donnees_forum ($idr, $idf, $ida, $idb, $ids) {
+
+	// changer la table de reference s'il y a lieu (pour afficher_groupes[] !!)
+	if ($idr) {
+		$r = "SELECT titre FROM spip_rubriques WHERE id_rubrique = $idr";
+		$table = "rubriques";
+	} else if ($ida) {
+		$r = "SELECT titre FROM spip_articles WHERE id_article = $ida";
+		$table = "articles";
+	} else if ($idb) {
+		$r = "SELECT titre FROM spip_breves WHERE id_breve = $idb";
+		$table = "breves";
+	} else if ($ids) {
+		$r = "SELECT nom_site AS titre FROM spip_syndic WHERE id_syndic = $ids";
+		$table = "syndic";
+	}
+
+	if ($idf)
+		$r = "SELECT titre FROM spip_forum WHERE id_forum = $idf";
+
+	if ($r)
+		list($titre) = spip_fetch_array(spip_query($r));
+	else {
+		$titre = _T('forum_titre_erreur');
+		$table = '';
+	}
+
+	// quelle est la configuration du forum ?
+	if ($ida)
+		list($accepter_forum) = spip_fetch_array(spip_query(
+		"SELECT accepter_forum FROM spip_articles WHERE id_article=$ida"));
+	else
+		$accepter_forum = substr(lire_meta("forums_publics"),0,3);
+	// valeurs possibles : 'pos'teriori, 'pri'ori, 'abo'nnement
+	if ($accepter_forum == "non")
+		return false;
+
+	return array ($titre, $table, $accepter_forum);
 }
 
 ?>
diff --git a/inc-html-squel.php3 b/inc-html-squel.php3
index ab65e97580..fda23b6c3e 100644
--- a/inc-html-squel.php3
+++ b/inc-html-squel.php3
@@ -47,7 +47,7 @@ function parser_texte($texte) {
 			$champ->params = '';
 		else {
 			if (!(ereg('^\\{(.*)\\}$', $p, $params))) {
-				include_local("inc-debug-squel.php3");
+				include_local("inc-admin.php3");
 				erreur_squelette(_L("Param&egrave;tres d'inclusion incorrects"),
 					$p, $champ->fichier);
 			}
@@ -211,7 +211,7 @@ function parser_param($params, &$result, $idb) {
 	}
 
 	if ($params) {
-		include_local("inc-debug-squel.php3");
+		include_local("inc-admin.php3");
 		erreur_squelette(_L("Param&egrave;tre $i (ou suivants) incorrect"),
 			$params, $idb);
 	}
@@ -233,7 +233,7 @@ function parser($texte, $id_parent, &$boucles) {
 		$debut = substr($texte, 0, $p);
 		$milieu = substr($texte, $p);
 		if (!ereg(BALISE_DE_BOUCLE, $milieu, $match)) {
-			include_local("inc-debug-squel.php3");
+			include_local("inc-admin.php3");
 			erreur_squelette((_T('erreur_boucle_syntaxe')), $milieu,'');
 		}
 		$id_boucle = $match[1];
@@ -267,7 +267,7 @@ function parser($texte, $id_parent, &$boucles) {
 		}
 		$milieu = substr($milieu, strlen($match[0]));
 		if (strpos($milieu, $s)) {
-			include_local("inc-debug-squel.php3");
+			include_local("inc-admin.php3");
 			erreur_squelette(_T('erreur_boucle_syntaxe'), '',
 				$id_boucle . 
 				_L('&nbsp;: balise B en aval'));
@@ -280,7 +280,7 @@ function parser($texte, $id_parent, &$boucles) {
 		$s = "</BOUCLE$id_boucle>";
 		$p = strpos($milieu, $s);
 		if ((!$p) && (substr($milieu, 0, strlen($s)) != $s)) {
-			include_local("inc-debug-squel.php3");
+			include_local("inc-admin.php3");
 			erreur_squelette(_T('erreur_boucle_syntaxe'), '',
 				_T('erreur_boucle_fermant',
 				array('id'=>$id_boucle)));
@@ -317,13 +317,12 @@ function parser($texte, $id_parent, &$boucles) {
 		$all_res = array_merge($all_res, parser_champs_etendus($debut));
 		$all_res[] = $result;
 		if ($boucles[$id_boucle]) {
-			include_local("inc-debug-squel.php3");
+			include_local("inc-admin.php3");
 			erreur_squelette(_T('erreur_boucle_syntaxe'), '',
 				_T('erreur_boucle_double',
 				array('id'=>$id_boucle)));
-			exit;
-		}
-		$boucles[$id_boucle] = $result;
+		} else
+			$boucles[$id_boucle] = $result;
 	}
 
 	return array_merge($all_res, parser_champs_etendus($texte));
diff --git a/inc-index-squel.php3 b/inc-index-squel.php3
index 6b847c720c..b057bc8bf9 100644
--- a/inc-index-squel.php3
+++ b/inc-index-squel.php3
@@ -37,7 +37,7 @@ function index_pile($idb, $nom_champ, &$boucles) {
 		#spip_log("Go: idb='$idb' r='$r' c='$c' nom='$nom_champ'");
 		$desc = $tables_principales[$t];
 		if (!$desc) {
-			include_local("inc-debug-squel.php3");
+			include_local("inc-admin.php3");
 			erreur_squelette(_L("Table SQL absente de \$tables_principales dans inc_serialbase"), $r, "'$idb'");
 		}
 		$excep = $exceptions_des_tables[$r][$c];
diff --git a/inc-messforum.php3 b/inc-messforum.php3
index dea4eafcac..ca8f89ce44 100644
--- a/inc-messforum.php3
+++ b/inc-messforum.php3
@@ -3,11 +3,12 @@
 include_ecrire('inc_texte.php3');
 include_ecrire('inc_filtres.php3');
 include_ecrire('inc_mail.php3');
+include_ecrire('inc_forum.php3');
+include_local('inc-forum.php3');
 
 if (file_exists("inc-urls.php3")) { include_local ("inc-urls.php3"); }
 else {include_local ("inc-urls-dist.php3"); }
 
-
 // Ce fichier inclus par inc-public a un comportement special
 // Voir commentaires dans celui-ci et dans inc-forum
 
@@ -25,7 +26,7 @@ $id_message = intval($id_message);
 
 // Nature du forum
 if (!$id_auteur)
-	$id_auteur = $GLOBALS['auteur_session']['id_auteur'];
+	$id_auteur = intval($GLOBALS['auteur_session']['id_auteur']);
 
 if ($forum_id_article) {
 	if ($s = spip_query("SELECT accepter_forum FROM spip_articles
@@ -85,12 +86,13 @@ $validation_finale = (strlen($confirmer) > 0 OR
 $statut = ((!$validation_finale) ? 'redac' : 
 	(($forums_publics == 'non') ? 'off' :
 	(($forums_publics == 'pri') ? 'prop' : 'publie')));
+
 spip_query("UPDATE spip_forum SET id_parent = $forum_id_parent,
 	id_rubrique =$forum_id_rubrique,
 	id_article = $forum_id_article,
 	id_breve = $forum_id_breve,
-	id_syndic = \"$forum_id_syndic\",
-	id_auteur = \"$id_auteur\",
+	id_syndic = $forum_id_syndic,
+	id_auteur = $id_auteur,
 	date_heure = NOW(),
 	titre = \"$slash_titre\",
 	texte = \"$slash_texte\",
@@ -103,6 +105,9 @@ spip_query("UPDATE spip_forum SET id_parent = $forum_id_parent,
 	WHERE id_forum = '$id_message'
 ");
 
+calculer_threads();
+
+
 if ($validation_finale) {
 	include_ecrire("inc_admin.php3");
 	if (!(verifier_action_auteur("ajout_forum $forum_id_rubrique".
@@ -119,7 +124,6 @@ if ($validation_finale) {
 		// INVALIDATION DES CACHES LIES AUX FORUMS
 		//
 		include_ecrire('inc_invalideur.php3');
-		include_ecrire('inc_forum.php3');
 		if ($statut == 'publie') {
 			suivre_invalideur ("id='id_forum/" .
 				calcul_index_forum($forum_id_article,
diff --git a/inc-public-global.php3 b/inc-public-global.php3
index af5377c472..1e5a6d0f85 100644
--- a/inc-public-global.php3
+++ b/inc-public-global.php3
@@ -114,7 +114,19 @@ function afficher_page_globale ($fond, $delais, &$use_cache) {
 		}
 	}
 
+	// Calculer le chemin putatif du cache
 	$chemin_cache = 'CACHE/'.generer_nom_fichier_cache('', $fond);
+
+	// Faut-il effacer des pages invalidees ?
+	if (lire_meta('invalider')) {
+		include_ecrire('inc_invalideur.php3');
+		include_ecrire('inc_meta.php3');
+		lire_metas();
+		if (lire_meta('invalider'))
+			retire_caches($chemin_cache);
+	}
+
+	// Peut-on utiliser un fichier cache ?
 	determiner_cache($delais, $use_cache, $chemin_cache);
 
 	// Repondre gentiment aux requetes sympas
@@ -280,7 +292,6 @@ function cherche_image_nommee($nom) {
 	}
 }
 
-
 // La fonction ci-dessous permet a un script de flusher ses resultats partiels
 function spip_ob_flush() {
 	if (!$GLOBALS['flag_ob']
diff --git a/inc-public.php3 b/inc-public.php3
index 2615fa667b..3934c2c82f 100644
--- a/inc-public.php3
+++ b/inc-public.php3
@@ -33,12 +33,19 @@ else {
 	// les meta
 	include_ecrire("inc_meta.php3");
 
-	// multilinguisme
+	// authentification du visiteur
 	if ($HTTP_COOKIE_VARS['spip_session'] OR
 	($PHP_AUTH_USER AND !$ignore_auth_http)) {
 		include_ecrire ("inc_session.php3");
 		verifier_visiteur();
 	}
+	// Faut-il preparer les boutons d'admin ?
+	if ($affiche_boutons_admin = (!$flag_preserver
+	AND $HTTP_COOKIE_VARS['spip_admin'])) {
+		include_local('inc-admin.php3');
+	}
+
+	// multilinguisme
 	if ($forcer_lang) {
 		include_ecrire('inc_lang.php3');
 		verifier_lang_url();
@@ -67,27 +74,10 @@ else {
 		reponse_confirmation($id_article);
 	}
 
-
-	// Faut-il effacer des pages invalidees ?
-	if (lire_meta('invalider')) {
-		include_ecrire('inc_invalideur.php3');
-		include_ecrire('inc_meta.php3');
-		lire_metas();
-		if (lire_meta('invalider'))
-			retire_caches();
-	}
-
 	include_local ("inc-public-global.php3");
 
 	$page = afficher_page_globale ($fond, $delais, $use_cache);
 
-
-	// Faut-il preparer les boutons d'admin ?
-	if ($affiche_boutons_admin = (!$flag_preserver
-	AND $HTTP_COOKIE_VARS['spip_admin'])) {
-		include_local('inc-admin.php3');
-	}
-
 	// Afficher la page ; le cas PHP est assez rigolo avec le traitement
 	// d'erreurs
 	if ($page['process_ins'] == 'php') {
diff --git a/inc-reqsql-squel.php3 b/inc-reqsql-squel.php3
index 81200123f9..8f33122d17 100644
--- a/inc-reqsql-squel.php3
+++ b/inc-reqsql-squel.php3
@@ -130,7 +130,8 @@ function calculer_requete(&$boucle) {
 		join('","', array_unique($boucle->from)) .
 		'"), # FROM
 		array(' .
-		(!$boucle->where ? '' : ( '"' . join('","', $boucle->where) . '"')) .
+		(!$boucle->where ? '' : ( '"' . join('",
+		"', $boucle->where) . '"')) .
 		"), # WHERE
 		'".addslashes($boucle->group)."', # GROUP
 		'".addslashes($boucle->order)."', # ORDER
diff --git a/inc-vrac-squel.php3 b/inc-vrac-squel.php3
index 9c26510430..a564ca72d5 100644
--- a/inc-vrac-squel.php3
+++ b/inc-vrac-squel.php3
@@ -200,7 +200,7 @@ function balise_COMPTEUR_BOUCLE_dist($p) {
 
 function balise_TOTAL_BOUCLE_dist($p) {
 	if ($p->id_mere === '') {
-		include_local("inc-debug-squel.php3");
+		include_local("inc-admin.php3");
 		erreur_squelette(_L("Champ #TOTAL_BOUCLE hors boucle"), '', $p->id_boucle);
 	}
 	$p->code = "\$Numrows['$p->id_mere']";
@@ -228,8 +228,8 @@ function balise_POINTS_dist($p) {
 	$b = $p->boucles[$b]->id_parent;
 	}
 	if (!$p->code) {
-	include_local("inc-debug-squel.php3");
-	erreur_squelette(_L("Champ #POINTS hors d'une recherche"), '', $p->id_boucle);
+		include_local("inc-admin.php3");
+		erreur_squelette(_L("Champ #POINTS hors d'une recherche"), '', $p->id_boucle);
 	}
 	$p->type = 'php';
 	return $p;
-- 
GitLab