From c81d961b972089fecdd667ac359182ef0ab22101 Mon Sep 17 00:00:00 2001
From: Fil <fil@rezo.net>
Date: Sun, 6 Apr 2008 01:43:26 +0000
Subject: [PATCH] Possibilite de passer des {doublons} dans les inclusions et
 dans le contexte general ; application : passer la gestion des documents du
 squelette article.html vers un inclure(inc-documents)

---
 dist/article.html              | 61 ++++++++++-----------------
 dist/inc-documents.html        | 77 ++++++++++++++++++++++++++++++++--
 ecrire/public/balises.php      | 10 +++++
 ecrire/public/compiler.php     | 44 ++++++++++++-------
 ecrire/public/composer.php     | 21 ++++++++++
 ecrire/public/phraser_html.php | 38 ++++++++---------
 ecrire/public/references.php   | 21 ++++++++--
 7 files changed, 190 insertions(+), 82 deletions(-)

diff --git a/dist/article.html b/dist/article.html
index ebc417cc88..3af2c4f2ec 100644
--- a/dist/article.html
+++ b/dist/article.html
@@ -42,50 +42,31 @@
 			[<div class="#EDIT{chapo} chapo">(#CHAPO|image_reduire{500,0})</div>]
 			[<div class="#EDIT{texte} texte entry-content">(#TEXTE|image_reduire{500,0})</div>]
 		</div>
-		
-		[(#REM) Portfolio : album d'images ]
-		<B_documents_portfolio>
-        <div id="documents_portfolio">
-            <h2><:info_portfolio:></h2>
-            <BOUCLE_documents_portfolio(DOCUMENTS) {id_article} {mode=document} {extension IN png,jpg,gif} {par num titre, date} {doublons}>[
-		<a href="(#URL_DOCUMENT)" type="#MIME_TYPE" onClick="location.href='[(#URL_ARTICLE|parametre_url{id_document,#ID_DOCUMENT})]#documents_portfolio';return false;"[ title="(#TITRE|couper{80}|texte_backend)"][ class="(#_article_principal:EXPOSE)"]>[(#FICHIER|copie_locale|image_reduire{0,60}|inserer_attribut{class,spip_logos}|inserer_attribut{alt,[(#TITRE|couper{80}|texte_backend)]})]</a>
-	]</BOUCLE_documents_portfolio>
-        </div>
-        </B_documents_portfolio>
-        <BOUCLE_afficher_document(DOCUMENTS) {id_article}{id_document} {mode=document}{extension IN png,jpg,gif}>
-        <div class="spip_documents spip_documents_center" id="document_actif">
-            [(#EMBED_DOCUMENT|image_reduire{500,0})]
-            [<div class="#EDIT{titre} spip_doc_titre">(#TITRE)</div>]
-            [<div class="#EDIT{descriptif} spip_doc_descriptif">(#DESCRIPTIF)</div>]
-        </div>
-        </BOUCLE_afficher_document>
-		
+
+
 		[<p class="#EDIT{hyperlien} hyperlien"><:voir_en_ligne:> : <a href="(#URL_SITE)" class="spip_out">[(#NOM_SITE|sinon{[(#URL_SITE|couper{80})]})]</a></p>]
         
 		[<div class="ps surlignable"><h2 class="pas_surlignable"><:info_ps:></h2><div class="#EDIT{ps}">(#PS|image_reduire{500,0})</div></div>]
 
-        [(#REM) Autres documents joints a l'article. Si un seul et texte vide, incrustation automatique ; a noter : le {!doublons A} permet de retrouver les documents dans la boucle suivante ]
-		<BOUCLE_documents_decompte(DOCUMENTS) {id_article} {mode=document}{doublons}{doublons A}
-		 />[(#TOTAL_BOUCLE|<>{1}|?{' ',#TEXTE**}|?{'',' '})
-		<INCLURE{fond=inc-documents}{id_article}>]
-		<//B_documents_decompte>
-		
-        <B_documents_joints>
-        <div class="menu" id="documents_joints">
-            <h2><:titre_documents_joints:></h2>
-            <ul>
-                <BOUCLE_documents_joints(DOCUMENTS){!doublons A} {par num titre, date}>[
-                <li>
-                    <strong><a href="(#URL_DOCUMENT)" title="<:bouton_telecharger:>" type="#MIME_TYPE">[(#TITRE|sinon{<:info_document:>})]</a></strong>
-                    <small>(<span>#TYPE_DOCUMENT[ &ndash; (#TAILLE|taille_en_octets)]</span>)</small>
-                    #DESCRIPTIF
-                ]</li>
-                </BOUCLE_documents_joints>
-            </ul>
-        </div>
-        </B_documents_joints>[
-        
-        (#REM) Petition :
+
+
+		[(#REM) Gestion du portfolio et des documents
+			Le critere d'inclusion {doublons} permet d'eviter de lister
+			 les documents deja integres dans la balise # TEXTE
+			Le critere d'incrustation permet d'incruster un document
+			 qui serait unique : on le fait si # TEXTE est vide
+			Le critere {env} permet de passer d'autres arguments de la page
+			 par exemple l'id_document choisi pour un affichage complet
+		]
+		[(#INCLURE{fond=inc-documents}
+			{id_article}
+			{doublons}
+			{incruster=[(#TEXTE**|=={''})]}
+			{env}
+		)]
+
+
+		[(#REM) Petition :
 		La petition ayant une PAGINATION il faut absolument {env}][
 	(#PETITION|?{' '})<INCLURE{fond=inc-petition}{id_article}{env}>]
 
diff --git a/dist/inc-documents.html b/dist/inc-documents.html
index 21fa7d4dcc..1854493a9c 100644
--- a/dist/inc-documents.html
+++ b/dist/inc-documents.html
@@ -1,3 +1,74 @@
-<BOUCLE1 (DOCUMENTS){id_article}{extension !IN png,jpg,gif}>
-<INCLURE{fond=modeles/emb}{id_document}>
-</BOUCLE1>
+
+	[(#REM) Portfolio : album d'images ]
+	<B_documents_portfolio>
+		<div id="documents_portfolio">
+		<h2><:info_portfolio:></h2>
+	<BOUCLE_documents_portfolio(DOCUMENTS) {id_article ?}
+	{mode=document}
+	{extension IN png,jpg,gif}
+	{par num titre, date}
+	{doublons}>[
+		<a href="(#URL_DOCUMENT)" type="#MIME_TYPE"
+		onClick="location.href='[(#URL_ARTICLE
+			|parametre_url{id_document,#ID_DOCUMENT}
+			)]#documents_portfolio';return false;"[
+		class="(#EXPOSER)"][
+		title="(#TITRE|couper{80}|texte_backend)"]>[(#FICHIER
+			|image_reduire{0,60}
+			|inserer_attribut{class,spip_logos}
+			|inserer_attribut{alt,[(#TITRE|couper{80}|texte_backend)]})]</a>
+	]</BOUCLE_documents_portfolio>
+		</div>
+	</B_documents_portfolio>
+
+	[(#REM)
+		Afficher en grand le document demande dans l'URL
+	]
+	<BOUCLE_afficher_document(DOCUMENTS)
+	{id_document}
+	{id_article ?}
+	{mode=document}
+	{extension IN png,jpg,gif}>
+	<div class="spip_documents spip_documents_center" id="document_actif">
+		[(#EMBED_DOCUMENT|image_reduire{500,0})]
+		[<div class="#EDIT{titre} spip_doc_titre">(#TITRE)</div>]
+		[<div class="#EDIT{descriptif} spip_doc_descriptif">(#DESCRIPTIF)</div>]
+		</div>
+	</BOUCLE_afficher_document>
+
+
+	[(#REM)
+		Si un seul document est joint a l'article, et si le texte est vide,
+		on incruste automatiquement ce document
+	]
+	<BOUCLE_documents_decompte(DOCUMENTS) {id_article ?}
+	{mode=document}
+	{doublons}
+	{doublons A}
+	>[(#TOTAL_BOUCLE|=={1}
+		|?{#ENV{incruster}}
+		|?{[(#INCLURE{fond=modeles/emb}{id_document}|trim)]})]</BOUCLE_documents_decompte>
+
+	[(#REM)
+		Liste des autres documents joints
+	]
+	<B_documents_joints>
+		<div class="menu" id="documents_joints">
+		<h2><:titre_documents_joints:></h2>
+		<ul>
+	<BOUCLE_documents_joints(DOCUMENTS)
+	{id_article ?}
+	{!doublons A} {par num titre, date}>[
+		<li>
+		<strong><a href="(#URL_DOCUMENT)" title="<:bouton_telecharger:>"
+			type="#MIME_TYPE">[(#TITRE|sinon{<:info_document:>})]</a></strong>
+		<small>(<span>#TYPE_DOCUMENT[ &ndash;
+			(#TAILLE|taille_en_octets)]</span>)</small>
+		#DESCRIPTIF
+		]</li>
+	</BOUCLE_documents_joints>
+		</ul>
+		</div>
+	</B_documents_joints>
+
+	<//B_documents_decompte>
diff --git a/ecrire/public/balises.php b/ecrire/public/balises.php
index 6d5c8a1d75..8ec251c9b1 100644
--- a/ecrire/public/balises.php
+++ b/ecrire/public/balises.php
@@ -1059,11 +1059,19 @@ function balise_INCLURE_dist($p) {
 	$_contexte = argumenter_inclure($champ, $p->descr, $p->boucles, $id_boucle, false);
 
 	if (isset($_contexte['fond'])) {
+
+		// Gerer ajax
 		if (isset($_contexte['ajax'])){
 			$_contexte['fond_ajax'] = preg_replace(",fond,","fond_ajax",$_contexte['fond'],1);
 			$_contexte['fond'] = "'fond' => 'fond/ajax_stat'";
 			unset($_contexte['ajax']);
 		}
+
+		// #INCLURE{doublons}
+		if (isset($_contexte['doublons'])) {
+			$_contexte['doublons'] = "'doublons' => \$doublons";
+		}
+
 		// Critere d'inclusion {env} (et {self} pour compatibilite ascendante)
 		if (isset($_contexte['env'])
 		|| isset($_contexte['self'])
@@ -1071,10 +1079,12 @@ function balise_INCLURE_dist($p) {
 			$flag_env = true;
 			unset($_contexte['env']);
 		} else $flag_env = false;
+
 		$l = 'array(' . join(",\n\t", $_contexte) .')';
 		if ($flag_env) {
 			$l = "array_merge(\$Pile[0],$l)";
 		}
+
 		$connect = !$id_boucle ? '' 
 		  : $p->boucles[$id_boucle]->sql_serveur;
 
diff --git a/ecrire/public/compiler.php b/ecrire/public/compiler.php
index 717d374924..80026a3478 100644
--- a/ecrire/public/compiler.php
+++ b/ecrire/public/compiler.php
@@ -68,12 +68,12 @@ function argumenter_inclure($struct, $descr, &$boucles, $id_boucle, $echap=true)
 // Calculer un <INCLURE()>
 //
 // http://doc.spip.org/@calculer_inclure
-function calculer_inclure($struct, $descr, &$boucles, $id_boucle) {
+function calculer_inclure($p, $descr, &$boucles, $id_boucle) {
 
 	# Si pas raccourci <INCLURE{fond=xxx}> 
 	# chercher le fichier, eventuellement en changeant.php3 => .php
 	# et en gardant la compatibilite <INCLURE(page.php3)>
-	if ($fichier = $struct->texte) {
+	if ($fichier = $p->texte) {
 		if (preg_match(',^(.*[.]php)3$,', $fichier, $r)) {
 			$fichier = $r[1];
 		}
@@ -91,7 +91,7 @@ function calculer_inclure($struct, $descr, &$boucles, $id_boucle) {
 		}
 	}
 
-	$_contexte = argumenter_inclure($struct, $descr, $boucles, $id_boucle);
+	$_contexte = argumenter_inclure($p, $descr, $boucles, $id_boucle);
 	if (isset($_contexte['fond']) && isset($_contexte['ajax'])){
 		$_contexte['fond_ajax'] = preg_replace(",fond,","fond_ajax",$_contexte['fond'],1);
 		$_contexte['fond'] = "\'fond\' => ' . argumenter_squelette('fond/ajax') . '";
@@ -100,6 +100,13 @@ function calculer_inclure($struct, $descr, &$boucles, $id_boucle) {
 	if ($env = (isset($_contexte['env'])|| isset($_contexte['self']))) {
 		unset($_contexte['env']);
 	}
+
+	// <INCLURE{doublons}>
+	if (isset($_contexte['doublons'])) {
+		// noter les doublons dans l'appel a public.php
+		$_contexte['doublons'] = "\\'doublons\\' => '.var_export(\$doublons,true).'";
+	}
+
 	$contexte = 'array(' . join(",\n\t", $_contexte) .')';
 	if ($env) {
 		$contexte = "array_merge('.var_export(\$Pile[0],1).',$contexte)";
@@ -107,9 +114,9 @@ function calculer_inclure($struct, $descr, &$boucles, $id_boucle) {
 
 	return "\n'<".
 		"?php\n\t\$contexte_inclus = $contexte;"
-		. "\n\tinclude(" .
+		. "\n\tinclude " .
 		($fichier ? "\\'$path\\'" : ('_DIR_RESTREINT . "public.php"')).
-		");" .
+		";" .
 		"\n?'." . "'>'";
  }
 
@@ -721,21 +728,22 @@ function public_compiler_dist($squelette, $nom, $gram, $sourcefile, $connect='')
 	include_spip('inc/charsets');
 	$squelette = transcoder_page($squelette);
 
+	// Informations sur le squelette
+	$descr = array('nom' => $nom, 'sourcefile' => $sourcefile,
+		'squelette' => $squelette);
+	unset($squelette);
+
 	// Phraser le squelette, selon sa grammaire
 	// pour le moment: "html" seul connu (HTML+balises BOUCLE)
 	$boucles = array();
 	spip_timer('calcul_skel');
 
-	$f = charger_fonction('phraser_'.$gram, 'public');
-
-	$racine = $f($squelette, '',$boucles, $nom);
 
-	// tableau des informations sur le squelette
-	$descr = array('nom' => $nom, 'sourcefile' => $sourcefile);
+	$f = charger_fonction('phraser_'.$gram, 'public');
 
-	// Signaler une boucle documents (les autres influent dessus)
-	// Et demander la description des tables une fois pour toutes
+	$racine = $f($descr['squelette'], '', $boucles, $nom);
 
+	// Demander la description des tables une fois pour toutes
 	foreach($boucles as $id => $boucle) {
 		$type = $boucle->type_requete;
 		if ($type != 'boucle') {
@@ -753,8 +761,6 @@ function public_compiler_dist($squelette, $nom, $gram, $sourcefile, $connect='')
 				if ((!$boucles[$id]->jointures)
 				AND (is_array($x = $tables_jointures[$nom_table])))
 					$boucles[$id]->jointures = $x;
-				if (($type == 'documents') && $boucle->doublons)
-					{ $descr['documents'] = true;  }
 			} else {
 				$boucles[$id]->type_requete = '';
 				$x = $boucles[$id]->sql_serveur;
@@ -769,6 +775,7 @@ function public_compiler_dist($squelette, $nom, $gram, $sourcefile, $connect='')
 			}
 		}
 	}
+
 	// Commencer par reperer les boucles appelees explicitement 
 	// car elles indexent les arguments de maniere derogatoire
 	foreach($boucles as $id => $boucle) { 
@@ -870,6 +877,13 @@ function public_compiler_dist($squelette, $nom, $gram, $sourcefile, $connect='')
 	  ($connect ? " pour $connect" : '') . ".
 //
 function " . $nom . '($Cache, $Pile, $doublons=array(), $Numrows=array(), $SP=0) {
+
+'
+	// reporter de maniere securisee les doublons inclus
+.'
+	if (is_array($Pile[0]["doublons"]))
+		$doublons = nettoyer_env_doublons($Pile[0]["doublons"]);
+
 	$connect = ' .
 	_q($connect) . ';
 	$page = ' .
@@ -884,7 +898,7 @@ function " . $nom . '($Cache, $Pile, $doublons=array(), $Numrows=array(), $SP=0)
 ?".">";
 
 	if (isset($GLOBALS['var_mode']) AND $GLOBALS['var_mode'] == 'debug')
-		squelette_debug_compile($nom, $sourcefile, $code, $squelette);
+		squelette_debug_compile($nom, $sourcefile, $code, $descr['squelette']);
 	return $code;
 
 }
diff --git a/ecrire/public/composer.php b/ecrire/public/composer.php
index fcdeb7a763..a9599c3665 100644
--- a/ecrire/public/composer.php
+++ b/ecrire/public/composer.php
@@ -482,6 +482,27 @@ function calculer_notes() {
 	return $r;
 }
 
+
+// Si un tableau &doublons[articles] est passe en parametre,
+// il faut le nettoyer car il pourrait etre injecte en SQL
+// http://doc.spip.org/@nettoyer_env_doublons
+function nettoyer_env_doublons($envd) {
+	foreach ($envd as $table => $liste) {
+		$n = '';
+		foreach(explode(',',$liste) as $val) {
+			if ($a = intval($val) AND $val === strval($a))
+				$n.= ','.$val;
+		}
+		if (strlen($n))
+			$envd[$table] = $n;
+		else
+			unset($envd[$table]);
+	}
+	return $envd;
+}
+
+
+
 // Ajouter "&lang=..." si la langue de base n'est pas celle du site.
 // Si le 2e parametre n'est pas une chaine, c'est qu'on n'a pas pu
 // determiner la table a la compil, on le fait maintenant.
diff --git a/ecrire/public/phraser_html.php b/ecrire/public/phraser_html.php
index 2459764111..93d104131a 100644
--- a/ecrire/public/phraser_html.php
+++ b/ecrire/public/phraser_html.php
@@ -48,24 +48,24 @@ function phraser_arguments_inclure($p,$rejet_filtres = false){
 			else
 				$champ->param[$k] = $v;
 		}
-		else {
-			if ($var->type != 'texte')
-				if ($rejet_filtres)
-					break; // on est arrive sur un filtre sans argument qui suit la balise
-				else
-					erreur_squelette(_T('zbug_parametres_inclus_incorrects'),$var);
-			else {
-				$champ->param[$k] = $v;
-				preg_match(",^([^=]*)(=)?(.*)$,", $var->texte,$m);
-				if ($m[2]) {
-					$champ->param[$k][0] = $m[1];
-					$val = $m[3];
-					if (preg_match(',^[\'"](.*)[\'"]$,', $val, $m)) $val = $m[1];
-					$champ->param[$k][1][0]->texte = $val;
-				}
-				else
-					$champ->param[$k] = array($m[1]);
+		else
+		if ($var->type != 'texte') {
+			if ($rejet_filtres)
+				break; // on est arrive sur un filtre sans argument qui suit la balise
+			else
+				erreur_squelette(_T('zbug_parametres_inclus_incorrects'),$var);
+		} else {
+			$champ->param[$k] = $v;
+			preg_match(",^([^=]*)(=)?(.*)$,", $var->texte,$m);
+			if ($m[2]) {
+				$champ->param[$k][0] = $m[1];
+				$val = $m[3];
+				if (preg_match(',^[\'"](.*)[\'"]$,', $val, $m)) $val = $m[1];
+				$champ->param[$k][1][0]->texte = $val;
 			}
+			else
+				$champ->param[$k] = array($m[1]);
+
 		}
 	}
 	return $champ;
@@ -90,9 +90,7 @@ function phraser_inclure($texte, $ligne, $result) {
 		$champ->param = $champ_->param;
 		$texte = substr($champ->apres, strpos($champ->apres, '>')+1);
 		$champ->apres = "";
-		if (preg_match(',^</INCLU[DR]E>,', $texte)) {	
-			$texte = substr($texte,10);
-		}
+		$texte = preg_replace(',^</INCLU[DR]E>,', '', $texte);
 		$result[] = $champ;
 	}
 	return (($texte==="") ? $result : phraser_idiomes($texte, $ligne, $result));
diff --git a/ecrire/public/references.php b/ecrire/public/references.php
index b5a5cff660..59f092fab0 100644
--- a/ecrire/public/references.php
+++ b/ecrire/public/references.php
@@ -296,6 +296,7 @@ function balise_distante_interdite($p) {
 // http://doc.spip.org/@champs_traitements
 function champs_traitements ($p) {
 	global $table_des_traitements;
+	static $test_doublons = array();
 
 	if (!isset($table_des_traitements[$p->nom_champ]))
 		return $p->code;
@@ -312,15 +313,27 @@ function champs_traitements ($p) {
 
 	if (!$ps) return $p->code;
 
-	// Si une boucle documents est presente dans le squelette, 
+
+	// Si une boucle DOCUMENTS{doublons} est presente dans le squelette,
+	// ou si in INCLURE contient {doublons}
 	// on insere une fonction de remplissage du tableau des doublons 
 	// dans les filtres propre() ou typo()
 	// (qui traitent les raccourcis <docXX> referencant les docs)
-	if (isset($p->descr['documents'])
-	AND ((strpos($ps,'propre') !== false)
-		OR (strpos($ps,'typo') !== false)))
+	// NB: le test permet d'eviter ce calcul quand on sait qu'il ne servira pas
+	if (!isset($test_doublons[$p->descr['sourcefile']]))
+		$test_doublons[$p->descr['sourcefile']]
+			= preg_match(',([<#]INCLU[RD]E|DOCUMENTS)[^<>]*{doublons,',
+				$p->descr['squelette']);
+
+	if ($test_doublons[$p->descr['sourcefile']]
+	AND (
+		(strpos($ps,'propre') !== false)
+		OR
+		(strpos($ps,'typo') !== false)
+	))
 		$ps = 'traiter_doublons_documents($doublons, '.$ps.')';
 
+
 	// Passer |safehtml sur les boucles "sensibles"
 	// sauf sur les champs dont on est surs
 	switch ($p->type_requete) {
-- 
GitLab