From 380ee519b379c38d8cb99ec8725a4af49d94e434 Mon Sep 17 00:00:00 2001
From: Cerdic <cedric@yterium.com>
Date: Tue, 20 Oct 2020 13:38:39 +0200
Subject: [PATCH] =?UTF-8?q?Fix=20le=20compilateur=20qui=20refusait=20d'uti?=
 =?UTF-8?q?liser=20des=20boucles=20dans=20des=20balises.=20Les=20syntaxes?=
 =?UTF-8?q?=20suivantes=20et=20leurs=20derivees=20possibles=20sont=20d?=
 =?UTF-8?q?=C3=A9sormais=20licites=20:=20[(#REM|non)=20=20=20=20=20<h3>Bal?=
 =?UTF-8?q?ise=202</h3>=20=20=20=20=20<BOUCLE=5Fpouet(ARTICLES){0,2}{tri?=
 =?UTF-8?q?=20#ENV{par,date}}>=20=20=20=20=20=20=20=20=20art:=20#ID=5FARTI?=
 =?UTF-8?q?CLE<br=20/>=20=20=20=20=20</BOUCLE=5Fpouet>=20]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

[(#REM|oui|?{<h3>Balise 2</h3>,
    <BOUCLE_pouet(ARTICLES){0,2}{tri #ENV{par,date}}>
        art: #ID_ARTICLE<br />
    </BOUCLE_pouet>})
]
---
 ecrire/inc_version.php         |  2 +-
 ecrire/public/phraser_html.php | 78 +++++++++++++++++++++++++++++-----
 2 files changed, 68 insertions(+), 12 deletions(-)

diff --git a/ecrire/inc_version.php b/ecrire/inc_version.php
index 2e47f4e457..5ebcdfc787 100644
--- a/ecrire/inc_version.php
+++ b/ecrire/inc_version.php
@@ -433,7 +433,7 @@ define('_SPIP_EXTRA_VERSION', '-dev');
 define('_DEV_VERSION_SPIP_COMPAT',"3.2.99");
 // version des signatures de fonctions PHP
 // (= date de leur derniere modif cassant la compatibilite et/ou necessitant un recalcul des squelettes)
-$spip_version_code = 20200930;
+$spip_version_code = 20201020;
 // version de la base SQL (= numero SVN de sa derniere modif)
 $spip_version_base = 24379;
 
diff --git a/ecrire/public/phraser_html.php b/ecrire/public/phraser_html.php
index e7f7afb7cf..483669ea89 100644
--- a/ecrire/public/phraser_html.php
+++ b/ecrire/public/phraser_html.php
@@ -502,6 +502,9 @@ function phraser_champs_interieurs($texte, $ligne, $sep, $result) {
 			}
 			$champ->apres = phraser_champs_exterieurs($debut, $n, $sep, $result);
 
+			// reinjecter la boucle si c'en est une
+			phraser_boucle_placeholder($champ);
+
 			$result[$i] = $champ;
 			$i++;
 			$texte = substr($texte, $p + strlen($match[0]));
@@ -869,11 +872,57 @@ function public_trouver_premiere_boucle($texte, $id_parent, $descr, $pos_debut_t
 	return $premiere_boucle;
 }
 
+/**
+ * @param object|string $champ
+ * @param null|string $boucle_placeholder
+ * @param null|object $boucle
+ */
+function phraser_boucle_placeholder(&$champ, $boucle_placeholder=null, $boucle = null) {
+	static $boucles_connues = array();
+	// si c'est un appel pour memoriser une boucle, memorisons la
+	if (is_string($champ) and !empty($boucle_placeholder) and !empty($boucle)) {
+		$boucles_connues[$boucle_placeholder][$champ] = &$boucle;
+	}
+	else {
+		if (!empty($champ->nom_champ) and !empty($boucles_connues[$champ->nom_champ])) {
+			$placeholder = $champ->nom_champ;
+			$id = reset($champ->param[0][1]);
+			$id = $id->texte;
+			if (!empty($boucles_connues[$placeholder][$id])) {
+				$champ = $boucles_connues[$placeholder][$id];
+			}
+		}
+	}
+}
+
+
+/**
+ * Generer une balise placeholder qui prend la place de la boucle pour continuer le parsing des balises
+ * @param string $id_boucle
+ * @param $boucle
+ * @param string $boucle_placeholder
+ * @param int $nb_lignes
+ * @return string
+ */
+function public_generer_boucle_placeholder($id_boucle, &$boucle, $boucle_placeholder, $nb_lignes) {
+	$placeholder = "[(#{$boucle_placeholder}{" . $id_boucle . '})' . str_pad("", $nb_lignes, "\n") . "]";
+	//memoriser la boucle a reinjecter
+	$id_boucle = "$id_boucle";
+	phraser_boucle_placeholder($id_boucle, $boucle_placeholder, $boucle);
+	return $placeholder;
+}
 
-function public_phraser_html_dist($texte, $id_parent, &$boucles, $descr, $ligne_debut_texte = 1) {
+function public_phraser_html_dist($texte, $id_parent, &$boucles, $descr, $ligne_debut_texte = 1, $boucle_placeholder = null) {
 
 	$all_res = array();
+	// definir un placholder pour les boucles dont on est sur d'avoir aucune occurence dans le squelette
+	if (is_null($boucle_placeholder)) {
+		do {
+			$boucle_placeholder = "BOUCLE_PLACEHOLDER_" . strtoupper(md5(uniqid()));
+		} while (strpos($texte, $boucle_placeholder) !== false);
+	}
 
+	$ligne_debut_initial = $ligne_debut_texte;
 	$pos_debut_texte = 0;
 	while ($boucle = public_trouver_premiere_boucle($texte, $id_parent, $descr, $pos_debut_texte)) {
 		$err_b = ''; // indiquera s'il y a eu une erreur
@@ -1046,18 +1095,18 @@ function public_phraser_html_dist($texte, $id_parent, &$boucles, $descr, $ligne_
 		}
 
 		$descr['id_mere_contexte'] = $id_boucle;
-		$result->milieu = public_phraser_html_dist($result->milieu, $id_boucle, $boucles, $descr, $ligne_milieu);
+		$result->milieu = public_phraser_html_dist($result->milieu, $id_boucle, $boucles, $descr, $ligne_milieu, $boucle_placeholder);
 		// reserver la place dans la pile des boucles pour compiler ensuite dans le bon ordre
 		// ie les boucles qui apparaissent dans les partie conditionnelles doivent etre compilees apres cette boucle
 		// si il y a deja une boucle de ce nom, cela declenchera une erreur ensuite
 		if (empty($boucles[$id_boucle])){
 			$boucles[$id_boucle] = null;
 		}
-		$result->preaff = public_phraser_html_dist($result->preaff, $id_parent, $boucles, $descr, $ligne_preaff);
-		$result->avant = public_phraser_html_dist($result->avant, $id_parent, $boucles, $descr, $ligne_avant);
-		$result->apres = public_phraser_html_dist($result->apres, $id_parent, $boucles, $descr, $ligne_apres);
-		$result->altern = public_phraser_html_dist($result->altern, $id_parent, $boucles, $descr, $ligne_altern);
-		$result->postaff = public_phraser_html_dist($result->postaff, $id_parent, $boucles, $descr, $ligne_postaff);
+		$result->preaff = public_phraser_html_dist($result->preaff, $id_parent, $boucles, $descr, $ligne_preaff, $boucle_placeholder);
+		$result->avant = public_phraser_html_dist($result->avant, $id_parent, $boucles, $descr, $ligne_avant, $boucle_placeholder);
+		$result->apres = public_phraser_html_dist($result->apres, $id_parent, $boucles, $descr, $ligne_apres, $boucle_placeholder);
+		$result->altern = public_phraser_html_dist($result->altern, $id_parent, $boucles, $descr, $ligne_altern, $boucle_placeholder);
+		$result->postaff = public_phraser_html_dist($result->postaff, $id_parent, $boucles, $descr, $ligne_postaff, $boucle_placeholder);
 
 		// Prevenir le generateur de code que le squelette est faux
 		if ($err_b) {
@@ -1078,14 +1127,21 @@ function public_phraser_html_dist($texte, $id_parent, &$boucles, $descr, $ligne_
 			$boucles[$id_boucle] = $result;
 		}
 
+		// remplacer la boucle par un placeholder qui compte le meme nombre de lignes
+		$placeholder = public_generer_boucle_placeholder($id_boucle, $boucles[$id_boucle], $boucle_placeholder, $ligne_suite - $ligne_debut_texte);
+		$longueur_boucle = $pos_courante - $boucle['debut_boucle'];
+		$texte = substr_replace($texte, $placeholder, $boucle['debut_boucle'], $longueur_boucle);
+		$pos_courante = $pos_courante - $longueur_boucle + strlen($placeholder);
+
 		// phraser la partie avant le debut de la boucle
-		$all_res = phraser_champs_etendus(substr($texte, $pos_debut_texte, $boucle['debut_boucle'] - $pos_debut_texte), $ligne_debut_texte, $all_res);
-		$all_res[] = &$boucles[$id_boucle];
+		#$all_res = phraser_champs_etendus(substr($texte, $pos_debut_texte, $boucle['debut_boucle'] - $pos_debut_texte), $ligne_debut_texte, $all_res);
+		#$all_res[] = &$boucles[$id_boucle];
 
 		$ligne_debut_texte = $ligne_suite;
 		$pos_debut_texte = $pos_courante;
 	}
 
-	$all_res = phraser_champs_etendus(substr($texte, $pos_debut_texte), $ligne_debut_texte, $all_res);
+	$all_res = phraser_champs_etendus($texte, $ligne_debut_initial, $all_res);
+
 	return $all_res;
-}
+}
\ No newline at end of file
-- 
GitLab