From 62a1f70c8653e8e084f55ef3cf77e74c49c40539 Mon Sep 17 00:00:00 2001
From: Fil <fil@rezo.net>
Date: Sat, 3 Jul 2004 03:12:58 +0000
Subject: [PATCH] backport labo + correction petits bugs sur id_version==0

---
 .gitattributes                    |   1 +
 ecrire/articles.php3              |  23 +-
 ecrire/articles_versions.php3     | 169 +++++++-------
 ecrire/img_pack/historique-24.gif | Bin 0 -> 775 bytes
 ecrire/inc_diff.php3              | 152 ++++++-------
 ecrire/inc_difflcs.php3           | 355 ++++++++++++++++++++++++++++++
 6 files changed, 525 insertions(+), 175 deletions(-)
 create mode 100644 ecrire/img_pack/historique-24.gif
 create mode 100644 ecrire/inc_difflcs.php3

diff --git a/.gitattributes b/.gitattributes
index 8c3b2d04f1..4258e61787 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -128,6 +128,7 @@ ecrire/img_pack/forum-droite_rtl.gif -text
 ecrire/img_pack/forum-interne-24.gif -text
 ecrire/img_pack/forum-vert.gif -text
 ecrire/img_pack/groupe-mot-24.gif -text
+ecrire/img_pack/historique-24.gif -text
 ecrire/img_pack/image-24.gif -text
 ecrire/img_pack/interface-display.gif -text
 ecrire/img_pack/jauge-fond.gif -text
diff --git a/ecrire/articles.php3 b/ecrire/articles.php3
index 5f28804394..27a520dded 100644
--- a/ecrire/articles.php3
+++ b/ecrire/articles.php3
@@ -232,16 +232,12 @@ if ($changer_virtuel && $flag_editable) {
 }
 
 if ($titre && !$ajout_forum && $flag_editable) {
-	$surtitre = addslashes(corriger_caracteres($surtitre));
-	$titre = addslashes(corriger_caracteres($titre));
-	$soustitre = addslashes(corriger_caracteres($soustitre));
-	$descriptif = addslashes(corriger_caracteres($descriptif));
-	$nom_site = addslashes(corriger_caracteres($nom_site));
-	$url_site = addslashes(corriger_caracteres($url_site));
-	$chapo = addslashes(corriger_caracteres($chapo));
-	$texte = addslashes(corriger_caracteres($texte));
-	$ps = addslashes(corriger_caracteres($ps));
-
+	$champs = array('surtitre', 'titre', 'soustitre', 'descriptif', 'nom_site', 'url_site', 'chapo', 'texte', 'ps');
+	$champs_version = array();
+	foreach ($champs as $nom_champ) {
+		$t = $champs_versions[$nom_champ] = corriger_caracteres($$nom_champ);
+		$$nom_champ = addslashes($t);
+	}
 
 	// recoller les champs du extra
 	if ($champs_extra) {
@@ -266,7 +262,7 @@ if ($titre && !$ajout_forum && $flag_editable) {
 	// Stockage des versions
 	if ($articles_versions != 'non') {
 		include_ecrire("inc_diff.php3");
-		ajouter_version($id_article, stripslashes($chapo), stripslashes($texte), stripslashes($ps), '');
+		ajouter_version($id_article, $champs_versions);
 	}
 
 	// Changer la langue heritee
@@ -338,6 +334,7 @@ if ($row = spip_fetch_array($result)) {
 	$referers = $row["referers"];
 	$extra = $row["extra"];
 	$id_trad = $row["id_trad"];
+	$id_version = $row["id_version"];
 }
 
 // pour l'affichage du virtuel
@@ -422,6 +419,10 @@ if ($connect_statut == "0minirezo" AND $statut_article == 'publie' AND $visites
 	icone_horizontale(_T('icone_evolution_visites', array('visites' => $visites)), "statistiques_visites.php3?id_article=$id_article", "statistiques-24.gif","rien.gif");
 }
 
+if ($articles_versions != 'non' AND $connect_statut == "0minirezo" AND $id_version>1) {
+	icone_horizontale(_L('Afficher les r&eacute;visions...'), "articles_versions.php3?id_article=$id_article", "historique-24.gif", "rien.gif");
+}
+
 echo "</div>\n";
 
 fin_boite_info();
diff --git a/ecrire/articles_versions.php3 b/ecrire/articles_versions.php3
index 41667595a5..46cde1218a 100644
--- a/ecrire/articles_versions.php3
+++ b/ecrire/articles_versions.php3
@@ -1,68 +1,23 @@
 <?php
 
-include ("inc.php3");
+include("inc.php3");
 
-include_ecrire ("inc_diff.php3");
+include_ecrire("inc_diff.php3");
 
-$articles_surtitre = lire_meta("articles_surtitre");
-$articles_soustitre = lire_meta("articles_soustitre");
-$articles_descriptif = lire_meta("articles_descriptif");
-$articles_urlref = lire_meta("articles_urlref");
-$articles_chapeau = lire_meta("articles_chapeau");
-$articles_ps = lire_meta("articles_ps");
-$articles_redac = lire_meta("articles_redac");
-$articles_mots = lire_meta("articles_mots");
-$articles_versions = lire_meta("articles_versions");
-
-$clean_link = new Link("articles.php3?id_article=$id_article");
-
-// Initialiser doublons pour documents (completes par "propre($texte)")
-$id_doublons['documents'] = "0";
-
-
-
-//////////////////////////////////////////////////////
-// Determiner les droits d'edition de l'article
-//
-
-$query = "SELECT statut, titre, id_rubrique FROM spip_articles WHERE id_article=$id_article";
-$result = spip_query($query);
-if ($row = spip_fetch_array($result)) {
-	$statut_article = $row['statut'];
-	$titre_article = $row['titre'];
-	$rubrique_article = $row['id_rubrique'];
-}
-else {
-	$statut_article = '';
-}
-
-$query = "SELECT * FROM spip_auteurs_articles WHERE id_article=$id_article AND id_auteur=$connect_id_auteur";
-$result_auteur = spip_query($query);
-
-$flag_auteur = (spip_num_rows($result_auteur) > 0);
-$flag_editable = (acces_rubrique($rubrique_article)
-	OR ($flag_auteur AND ($statut_article == 'prepa' OR $statut_article == 'prop' OR $statut_article == 'poubelle')));
 
 
 //
 // Lire l'article
 //
 
+$champs = array('surtitre', 'titre', 'soustitre', 'descriptif', 'nom_site', 'url_site', 'chapo', 'texte', 'ps');
+
 $query = "SELECT * FROM spip_articles WHERE id_article='$id_article'";
 $result = spip_query($query);
 
 if ($row = spip_fetch_array($result)) {
 	$id_article = $row["id_article"];
-	$surtitre = $row["surtitre"];
-	$titre = $row["titre"];
-	$soustitre = $row["soustitre"];
 	$id_rubrique = $row["id_rubrique"];
-	$descriptif = $row["descriptif"];
-	$nom_site = $row["nom_site"];
-	$url_site = $row["url_site"];
-	$chapo = $row["chapo"];
-	$texte = $row["texte"];
-	$ps = $row["ps"];
 	$date = $row["date"];
 	$statut_article = $row["statut"];
 	$maj = $row["maj"];
@@ -73,19 +28,54 @@ if ($row = spip_fetch_array($result)) {
 	$id_trad = $row["id_trad"];
 }
 
-if (ereg("([0-9]{4})-([0-9]{2})-([0-9]{2})", $date_redac, $regs)) {
-        $mois_redac = $regs[2];
-        $jour_redac = $regs[3];
-        $annee_redac = $regs[1];
-        if ($annee_redac > 4000) $annee_redac -= 9000;
+if (!($id_version = intval($id_version))) {
+	$id_version = intval($row['id_version']);
 }
+$textes = recuperer_version($id_article, $id_version);
+
+$id_diff = intval($id_diff);
+if (!$id_diff) {
+	$diff_auto = true;
+	$query = "SELECT id_version FROM spip_versions WHERE id_article=$id_article ".
+		"AND id_version<$id_version ORDER BY id_version DESC LIMIT 0,1";
+	if ($result = spip_query($query)) {
+		$row = mysql_fetch_array($result);
+		$id_diff = $row['id_version'];
+	}
+}
+
+//
+// Calculer le diff
+//
+
+if ($id_version && $id_diff) {
+	include_ecrire("inc_difflcs.php3");
+
+	if ($id_diff > $id_version) {
+		$t = $id_version;
+		$id_version = $id_diff;
+		$id_diff = $t;
+		$old = $textes;
+		$new = $textes = recuperer_version($id_article, $id_version);
+	}
+	else {
+		$old = recuperer_version($id_article, $id_diff);
+		$new = $textes;
+	}
+
+	$textes = array();
+	
+	foreach ($champs as $champ) {
+		if (!$new[$champ] && !$old[$champ]) continue;
 
-if (ereg("([0-9]{4})-([0-9]{2})-([0-9]{2})", $date, $regs)) {
-        $mois = $regs[2];
-        $jour = $regs[3];
-        $annee = $regs[1];
+		$diff = new Diff(new DiffTexte);
+		$textes[$champ] = afficher_diff($diff->comparer(preparer_diff($new[$champ]), preparer_diff($old[$champ])));
+	}
 }
 
+if (is_array($textes))
+foreach ($textes as $var => $t) $$var = $t;
+
 
 
 debut_page("&laquo; $titre_article &raquo;", "documents", "articles");
@@ -93,7 +83,7 @@ debut_page("&laquo; $titre_article &raquo;", "documents", "articles");
 debut_grand_cadre();
 
 afficher_parents($id_rubrique);
-$parents="~ <img src='img_pack/racine-site-24.gif' width=24 height=24 align='middle'> <A HREF='naviguer.php3?coll=0'><B>"._T('lien_racine_site')."</B></A> ".aide ("rubhier")."<BR>".$parents;
+$parents="~ <img src='img_pack/racine-site-24.gif' width=24 height=24 align='middle' alt='' /> <a href='naviguer.php3?coll=0'><b> "._T('lien_racine_site')."</b> </a>  ".aide ("rubhier")."<br /> ".$parents;
 $parents=ereg_replace("~","&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;",$parents);
 $parents=ereg_replace("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ","",$parents);
 echo "$parents";
@@ -116,10 +106,9 @@ debut_gauche();
 
 debut_droite();
 
-
 changer_typo('','article'.$id_article);
 
-
+echo "<a name='diff'></a>\n";
 
 debut_cadre_relief();
 
@@ -144,17 +133,17 @@ else if ($statut_article == 'poubelle') {
 }
 
 
-echo "\n<table cellpadding=0 cellspacing=0 border=0 width='100%'>";
+echo "\n<table cellpadding='0' cellspacing='0' border='0' width='100%'>";
 echo "<tr width='100%'><td width='100%' valign='top'>";
 if ($surtitre) {
-	echo "<span $dir_lang><font face='arial,helvetica' size=3><b>";
+	echo "<span $dir_lang><font face='arial,helvetica' size='3'><b>";
 	echo typo($surtitre);
 	echo "</b></font></span>\n";
 }
-	gros_titre($titre, $logo_statut);
+gros_titre($titre, $logo_statut);
 
 if ($soustitre) {
-	echo "<span $dir_lang><font face='arial,helvetica' size=3><b>";
+	echo "<span $dir_lang><font face='arial,helvetica' size='3'><b>";
 	echo typo($soustitre);
 	echo "</b></font></span>\n";
 }
@@ -162,14 +151,21 @@ if ($soustitre) {
 
 if ($descriptif OR $url_site OR $nom_site) {
 	echo "<p><div align='left' style='padding: 5px; border: 1px dashed #aaaaaa; background-color: #e4e4e4;' $dir_lang>";
-	echo "<font size=2 face='Verdana,Arial,Sans,sans-serif'>";
+	echo "<font size='2' face='Verdana,Arial,Sans,sans-serif'>";
 	$texte_case = ($descriptif) ? "{{"._T('info_descriptif')."}} $descriptif\n\n" : '';
 	$texte_case .= ($nom_site.$url_site) ? "{{"._T('info_urlref')."}} [".$nom_site."->".$url_site."]" : '';
 	echo propre($texte_case);
 	echo "</font>";
 	echo "</div>";
 }
-echo "</td></tr></table>";
+
+echo "</td>";
+
+echo "<td align='center'>";
+icone(_L("Retour &agrave; l'article"), "articles.php3?id_article=$id_article", "", "article-24.gif");
+echo "</td>";
+
+echo "</tr></table>";
 
 
 //////////////////////////////////////////////////////
@@ -179,23 +175,40 @@ echo "</td></tr></table>";
 debut_cadre_relief();
 
 $query = "SELECT id_version, date, v.id_auteur, a.nom FROM spip_versions AS v, spip_auteurs AS a ".
-	"WHERE id_article=$id_article AND v.id_auteur=a.id_auteur ORDER BY date";
+	"WHERE id_article=$id_article AND v.id_auteur=a.id_auteur ORDER BY date DESC";
 $result = spip_query($query);
 
-echo "<ul>";
+echo "<ul class='verdana3'>";
 while ($row = spip_fetch_array($result)) {
 	echo "<li>\n";
 	$date = affdate_heure($row['date']);
-	if ($row['id_version'] != $id_version) {
+	$version_aff = $row['id_version'];
+	if ($version_aff != $id_version) {
 		$link = new Link();
-		$link->addVar('id_version', $row['id_version']);
-		echo "<a href='".$link->getUrl()."'>$date</a>";
+		$link->addVar('id_version', $version_aff);
+		$link->delVar('id_diff');
+		echo "<a href='".$link->getUrl('diff')."' title=\"Afficher cette version\">$date</a>";
 	}
 	else {
 		echo "<b>$date</b>";
 	}
 	echo " (".$row['nom'].")";
-	echo " <span style='color:#989898; font-size: 80%; font-weight: bold;'><i>#".$row['id_version']."</i></span>";
+	if ($options == 'avancees') {
+		//echo " <span style='color:#989898; font-size: 80%; font-weight: bold;'><i>#".$row['id_version']."</i></span>";
+		if ($version_aff != $id_version) {
+			echo " <span class='verdana2'>";
+			if ($version_aff == $id_diff) {
+				echo "<b>(comparaison)</b>";
+			}
+			else {
+				$link = new Link();
+				$link->addVar('id_version', $id_version);
+				$link->addVar('id_diff', $version_aff);
+				echo "(<a href='".$link->getUrl('diff')."' title=\"Afficher les modifications d'avec cette version\">comparaison</a>)";
+			}
+			echo "</span>";
+		}
+	}
 	echo "</li>\n";
 }
 echo "</ul>\n";
@@ -210,11 +223,6 @@ fin_cadre_relief();
 if ($id_version) {
 	echo "\n\n<div align='justify'>";
 
-	$champs = recuperer_version($id_article, $id_version);
-	$chapo = $champs['chapo'];
-	$texte = $champs['texte'];
-	$ps = $champs['ps'];
-
 	// pour l'affichage du virtuel
 	unset($virtuel);
 	if (substr($chapo, 0, 1) == '=') {
@@ -238,7 +246,7 @@ if ($id_version) {
 	
 		if ($ps) {
 			echo debut_cadre_enfonce();
-			echo "<div $dir_lang><font size=2 face='Verdana,Arial,Sans,sans-serif'>";
+			echo "<div $dir_lang><font size='2' face='Verdana,Arial,Sans,sans-serif'>";
 			echo justifier("<b>"._T('info_ps')."</b> ".propre($ps));
 			echo "</font></div>";
 			echo fin_cadre_enfonce();
@@ -247,7 +255,7 @@ if ($id_version) {
 	
 		if ($les_notes) {
 			echo debut_cadre_relief();
-			echo "<div $dir_lang><font size=2>";
+			echo "<div $dir_lang><font size='2'>";
 			echo justifier("<b>"._T('info_notes')."&nbsp;:</b> ".$les_notes);
 			echo "</font></div>";
 			echo fin_cadre_relief();
@@ -266,4 +274,3 @@ fin_cadre_relief();
 fin_page();
 
 ?>
-
diff --git a/ecrire/img_pack/historique-24.gif b/ecrire/img_pack/historique-24.gif
new file mode 100644
index 0000000000000000000000000000000000000000..0d34cb6c76f2b620bf137b071e0f7226abb24151
GIT binary patch
literal 775
zcmZ?wbhEHblwgoxI2OwQ1gxyAVq#+A;^Jy*YWn*61_lO(hK434CYF|#wzjtR_V(`X
z?s0K({V8l285uLO*()n6YieqCG;#Ix^z`=jPM9!Z<7E3yQ(dM`ojPsWw2M=CX3d&4
zckbM)b7c-J@VLHA=jL*~1M7>|uU~(0L*RxD8;)<v+O%oY=FOY8Y}vAP>((>dbGL2V
zc6EE~{q0^4w)^bdxpVjK-Fx=z*}He|zJ2=+95`_EaLMuG$4{I%aq{HJvuDq~yVh{~
z_U*fO?>>C^@X@13@1JaY{P^+vr@Nj!dGhq>)A!F0K701;`Sa&5Uc7ku^5v^nuU@}?
z{pQV^w{PFRd-v}B`}ZF{eE9hB<EKxbK7aoF<;$0^U%!6)_U-%k?>~P0`1$kauV24@
z|Ni~w&!7MF0*e1cor_WvOHxx5$}>wc6hbmm72G|20~i#4vM_Qn)HCRSq6idO4DA0J
z>YJJwn%mkt+87u*dpj7M82TpHGq_rJGYHyDX=8A(UTCc=!5}%ep22C+8Wje~DGW+$
zw#YNgWe||wy+_~LjDd-fk%iy9lYwu~9yx1qhN#FuOHO_uS9+#^w4OFYxM@Uape#=(
zg8)N47lW{(HA9SMWLS`|6n|&8Ifn>?bQ!~gLyX>{n!Xbxc1+|r&@2(m&|tvW(9NO~
zy2!+_=#ir`llu~vh?XYrX6=wgI+mB39TqeAqy-$@wDcL1Z15b7L_rmxh<A#|$|6>7
zF&X9z1J6Z|+#B3j+AJECTDw9ujTsI&IJK}hy75Qo6n;|ax~cA$z+fP><Vdrdk2#-0
z<e?><lhu5tsaP5vac`9FlZ$Ye<kI<2#iNJkXW&CdRW`8>8OuXXtP>UNv<w`+9yrh`
dXJ7Y0BJcsDmZQ0x0Ea-sfoAFD$?QxF)&NI>mg@ij

literal 0
HcmV?d00001

diff --git a/ecrire/inc_diff.php3 b/ecrire/inc_diff.php3
index 1e35a941d0..18ce8760a2 100644
--- a/ecrire/inc_diff.php3
+++ b/ecrire/inc_diff.php3
@@ -6,7 +6,6 @@ if (defined("_ECRIRE_INC_DIFF")) return;
 define("_ECRIRE_INC_DIFF", "1");
 
 
-
 function separer_paras($texte, $paras = "") {
 	if (!$paras) $paras = array();
 	while (preg_match("/(\r\n?){2,}|\n{2,}/", $texte, $regs)) {
@@ -39,8 +38,8 @@ function ajouter_fragments($id_article, $id_version, $fragments) {
 			$fragment = unserialize($fragment);
 			if (is_array($fragment)) {
 				unset($fragment[$id_version]);
-				// Si moins de cinq revisions distinctes dans le fragment, prolonger celui-ci
-				if (count($fragment) < 5) $nouveau = false;
+				// Si le fragment n'est pas trop gros, prolonger celui-ci
+				$nouveau = count($fragment) > 8 && strlen($row['fragment']) > 1000;
 			}
 		}
 		if ($nouveau) {
@@ -66,7 +65,7 @@ function ajouter_fragments($id_article, $id_version, $fragments) {
 		if ($flag_gz) {
 			$s = gzcompress($fragment);
 			if (strlen($s) < strlen($fragment)) {
-				//echo "gain gz: ".(100 - 100 * strlen($s) / strlen($fragment))."%<br>";
+				//echo "gain gz: ".(100 - 100 * strlen($s) / strlen($fragment))."%<br />";
 				$compress = 1;
 				$fragment = $s;
 			}
@@ -88,6 +87,7 @@ function ajouter_fragments($id_article, $id_version, $fragments) {
 // renvoie un tableau associatif (id_fragment => texte)
 //
 function recuperer_fragments($id_article, $id_version) {
+	$id_version = intval($id_version);
 	$fragments = array();
 
 	$query = "SELECT id_fragment, version_min, compress, fragment FROM spip_versions_fragments ".
@@ -115,7 +115,7 @@ function recuperer_fragments($id_article, $id_version) {
 // Apparier des paragraphes deux a deux entre une version originale
 // et une version modifiee
 //
-function apparier_paras($src, $dest) {
+function apparier_paras($src, $dest, $flou = true) {
 	$src_dest = array();
 	$dest_src = array();
 	
@@ -134,67 +134,55 @@ function apparier_paras($src, $dest) {
 		$t2[$key] = preg_replace("/[[:punct:][:space:]]+/", " ", $val);
 	}
 
-	// Hash pour premiere passe
-	foreach($t1 as $key => $val) $md1[md5($val)] = $key;
-	foreach($t2 as $key => $val) $md2[md5($val)] = $key;
-
 	// Premiere passe : chercher les correspondance exactes
-	foreach($md1 as $h => $key1) {
-		if (isset($md2[$h])) {
-			$key2 = $md2[$h];
+	foreach($t1 as $key => $val) $md1[$key] = md5($val);
+	foreach($t2 as $key => $val) $md2[md5($val)][$key] = $key;
+	foreach($md1 as $key1 => $h) {
+		if (count($md2[$h])) {
+			$key2 = reset($md2[$h]);
 			if ($t1[$key1] == $t2[$key2]) {
 				$src_dest[$key1] = $key2;
 				$dest_src[$key2] = $key1;
 				unset($t1[$key1]);
 				unset($t2[$key2]);
+				unset($md2[$h][$key2]);
 			}
 		}
 	}
 
-	// Deuxieme passe : recherche de correlation par test de compressibilite
-	foreach($t1 as $key => $val) {
-		$l1[$key] = strlen(gzcompress($val));
-	}
-	foreach($t2 as $key => $val) {
-		$l2[$key] = strlen(gzcompress($val));
-	}
-	foreach($t1 as $key1 => $s1) {
-		//echo "<br>";
-		foreach($t2 as $key2 => $s2) {
-			$r = strlen(gzcompress($s1.$s2));
-			//$k += strlen($s1) + strlen($s2);
-			$taux = 1.0 * $r / ($l1[$key1] + $l2[$key2]);
-			//echo "<li>$key1 => $key2 : $taux</li>";
-			if (!$gz_min1[$key1] || $gz_min1[$key1] > $taux) {
-				$gz_min1[$key1] = $taux;
-				$gz_trans1[$key1] = $key2;
-			}
-			if (!$gz_min2[$key2] || $gz_min2[$key2] > $taux) {
-				$gz_min2[$key2] = $taux;
-				$gz_trans2[$key2] = $key1;
+	if ($flou) {
+		// Deuxieme passe : recherche de correlation par test de compressibilite
+		foreach($t1 as $key => $val) {
+			$l1[$key] = strlen(gzcompress($val));
+		}
+		foreach($t2 as $key => $val) {
+			$l2[$key] = strlen(gzcompress($val));
+		}
+		foreach($t1 as $key1 => $s1) {
+			foreach($t2 as $key2 => $s2) {
+				$r = strlen(gzcompress($s1.$s2));
+				$taux = 1.0 * $r / ($l1[$key1] + $l2[$key2]);
+				if (!$gz_min1[$key1] || $gz_min1[$key1] > $taux) {
+					$gz_min1[$key1] = $taux;
+					$gz_trans1[$key1] = $key2;
+				}
+				if (!$gz_min2[$key2] || $gz_min2[$key2] > $taux) {
+					$gz_min2[$key2] = $taux;
+					$gz_trans2[$key2] = $key1;
+				}
 			}
 		}
-	}
-	//echo "$k octets compresses<p>";
-	
-	// Depouiller les resultats de la deuxieme passe :
-	// ne retenir que les correlations reciproques
-	foreach($gz_trans1 as $key1 => $key2) {
-		if ($gz_trans2[$key2] == $key1 && $gz_min1[$key1] < 0.9) {
-			$src_dest[$key1] = $key2;
-			$dest_src[$key2] = $key1;
+		
+		// Depouiller les resultats de la deuxieme passe :
+		// ne retenir que les correlations reciproques
+		foreach($gz_trans1 as $key1 => $key2) {
+			if ($gz_trans2[$key2] == $key1 && $gz_min1[$key1] < 0.9) {
+				$src_dest[$key1] = $key2;
+				$dest_src[$key2] = $key1;
+			}
 		}
 	}
 
-	/*echo "<br>";
-	foreach ($gz_trans1 as $a => $b) {
-		echo "$a => $b<br>";
-		echo "<blockquote><div style='border: 1px solid black'>".$t1[$a]."</div>";
-		echo "<div style='border: 1px solid black'>".$t2[$b]."</div></blockquote>";
-	}
-	echo "<br>";
-	foreach ($gz_trans2 as $b => $a) echo "$a $b<br>";*/
-
 	// Retourner les mappings
 	return array($src_dest, $dest_src);
 }
@@ -204,23 +192,21 @@ function apparier_paras($src, $dest) {
 // Recuperer les champs d'une version donnee
 //
 function recuperer_version($id_article, $id_version) {
-	$query = "SELECT chapo, texte, ps, extra FROM spip_versions ".
+	if (!$id_version) $id_version = '0';
+	$query = "SELECT champs FROM spip_versions ".
 		"WHERE id_article=$id_article AND id_version=$id_version";
 	$result = spip_query($query);
 	
 	if (!($row = spip_fetch_array($result))) return false;
 
-	$codes['chapo'] = $row['chapo'];
-	$codes['texte'] = $row['texte'];
-	$codes['ps'] = $row['ps'];
-	
 	$fragments = recuperer_fragments($id_article, $id_version);
+	$champs = unserialize($row['champs']);
 	$textes = array();
-	foreach ($codes as $var => $code) {
-		$textes[$var] = "";
+	foreach ($champs as $nom_champ => $code) {
+		$textes[$nom_champ] = "";
 		$code = explode(' ', $code);
 		foreach ($code as $id_fragment) {
-			$textes[$var] .= $fragments[$id_fragment];
+			$textes[$nom_champ] .= $fragments[$id_fragment];
 		}
 	}
 	return $textes;
@@ -229,15 +215,15 @@ function recuperer_version($id_article, $id_version) {
 //
 // Ajouter une version a un article
 //
-function ajouter_version($id_article, $chapo, $texte, $ps, $extra) {
+function ajouter_version($id_article, $champs) {
 	global $connect_id_auteur;
 
 	// Eviter les validations entremelees
 	$lock = "ajout_version $id_article";
-	spip_get_lock($lock, 5);
+	spip_get_lock($lock, 10);
 	
 	// Examiner la derniere version
-	$query = "SELECT id_version, (id_auteur=$connect_id_auteur AND date > DATE_SUB(NOW(), INTERVAL 1 HOUR) AND permanent!='oui') AS flag ".
+	$query = "SELECT id_version, (id_auteur=$connect_id_auteur AND date > DATE_SUB(NOW(), INTERVAL 1 SECOND) AND permanent!='oui') AS flag ".
 		"FROM spip_versions WHERE id_article=$id_article ".
 		"ORDER BY id_version DESC LIMIT 0,1";
 	$result = spip_query($query);
@@ -262,46 +248,46 @@ function ajouter_version($id_article, $chapo, $texte, $ps, $extra) {
 	// Generer les nouveaux fragments
 	$fragments = array();
 	$paras_old = recuperer_fragments($id_article, $id_version);
-	$paras_new = $paras_var = array();
-	$vars = array('chapo', 'texte', 'ps');
-	foreach ($vars as $var) {
-		$codes[$var] = array();
-		$paras_new = separer_paras($$var, $paras_new);
-		$paras_var[$var] = count($paras_new);
+	$paras_new = $paras_champ = array();
+	foreach ($champs as $nom_champ => $texte) {
+		$codes[$nom_champ] = array();
+		$paras_new = separer_paras($texte, $paras_new);
+		$paras_champ[$nom_champ] = count($paras_new);
 	}
 
 	// Apparier les fragments de maniere optimale
 	$n = count($paras_new);
 	if ($n) {
+		// Tables d'appariement dans les deux sens
 		list($trans, $trans_rev) = apparier_paras($paras_old, $paras_new);
-		reset($vars);
-		$var = '';
+		reset($champs);
+		$nom_champ = '';
 		for ($i = 0; $i < $n; $i++) {
-			while ($i >= $paras_var[$var]) list(, $var) = each($vars);
+			while ($i >= $paras_champ[$nom_champ]) list($nom_champ, ) = each($champs);
 			// Lier au fragment existant si possible, sinon creer un nouveau fragment
 			if (isset($trans_rev[$i])) $id_fragment = $trans_rev[$i];
 			else $id_fragment = $id_fragment_next++;
-			$codes[$var][] = $id_fragment;
+			$codes[$nom_champ][] = $id_fragment;
 			$fragments[$id_fragment] = $paras_new[$i];
 		}
 	}
-	foreach ($vars as $var) $codes[$var] = join(' ', $codes[$var]);
+	foreach ($champs as $nom_champ => $t) {
+		$codes[$nom_champ] = join(' ', $codes[$nom_champ]);
+		if (!strlen($codes[$nom_champ])) unset($codes[$nom_champ]);
+	}
 
 	// Enregistrer les modifications
 	ajouter_fragments($id_article, $id_version_new, $fragments);
-	$code_chapo = addslashes($codes['chapo']);
-	$code_texte = addslashes($codes['texte']);
-	$code_ps = addslashes($codes['ps']);
+	if (!$codes) $codes = array();
+	$codes = addslashes(serialize($codes));
 	if ($nouveau) {
-		$query = "INSERT spip_versions (id_article, id_version, permanent, date, id_auteur, chapo, texte, ps) ".
-			"VALUES ($id_article, $id_version_new, 'non', NOW(), '$connect_id_auteur', '$code_chapo', ".
-			"'$code_texte', '$code_ps')";
+		$query = "INSERT spip_versions (id_article, id_version, permanent, date, id_auteur, champs) ".
+			"VALUES ($id_article, $id_version_new, 'non', NOW(), '$connect_id_auteur', '$codes')";
 		spip_query($query);
 	}
 	else {
-		$query = "UPDATE spip_versions SET date=NOW(), id_auteur=$connect_id_auteur, ".
-			"chapo='$code_chapo', texte='$code_texte', ps='$code_ps' ".
-			"WHERE id_article=$id_article AND id_version=$id_version";
+		$query = "UPDATE spip_versions SET date=NOW(), id_auteur=$connect_id_auteur, champs='$codes' ".
+		 	"WHERE id_article=$id_article AND id_version=$id_version";
 		spip_query($query);
 	}
 	$query = "UPDATE spip_articles SET id_version=$id_version_new WHERE id_article=$id_article";
@@ -313,4 +299,4 @@ function ajouter_version($id_article, $chapo, $texte, $ps, $extra) {
 }
 
 
-?>
\ No newline at end of file
+?>
diff --git a/ecrire/inc_difflcs.php3 b/ecrire/inc_difflcs.php3
new file mode 100644
index 0000000000..f368be85ba
--- /dev/null
+++ b/ecrire/inc_difflcs.php3
@@ -0,0 +1,355 @@
+<?php
+
+// Ce fichier ne sera execute qu'une fois
+if (defined("_ECRIRE_INC_DIFFLCS")) return;
+define("_ECRIRE_INC_DIFFLCS", "1");
+
+
+//
+// LCS (Longest Common Subsequence) en deux versions
+// (ref: http://www2.toki.or.id/book/AlgDesignManual/BOOK/BOOK5/NODE208.HTM)
+
+// Version ultra-simplifiee : chaque chaine est une permutation de l'autre 
+// et on passe en parametre un des deux tableaux de correspondances
+function lcs_opt($s) {
+	$n = count($s);
+	if (!$n) return array();
+	$paths = array();
+	$paths_ymin = array();
+	$max_len = 0;
+
+	// Insertion des points
+	asort($s);
+	foreach ($s as $y => $c) {
+		for ($len = $max_len; $len > 0; $len--) {
+			if ($paths_ymin[$len] < $y) {
+				$paths_ymin[$len + 1] = $y;
+				$paths[$len + 1] = $paths[$len];
+				$paths[$len + 1][$y] = $c;
+				break;
+			}
+		}
+		if ($len == 0) {
+			$paths_ymin[1] = $y;
+			$paths[1] = array($y => $c);
+		}
+		if ($len + 1 > $max_len) $max_len = $len + 1;
+	}
+	return $paths[$max_len];
+}
+
+// Version normale : les deux chaines n'ont pas ete traitees au prealable
+// par la fonction d'appariement
+function lcs($s, $t) {
+	$n = count($s);
+	$p = count($t);
+	if (!$n || !$p) return array(0 => array(), 1 => array());
+	$paths = array();
+	$paths_ymin = array();
+	$max_len = 0;
+	$s_pos = $t_pos = array();
+
+	// Insertion des points
+	foreach ($t as $y => $c) $t_pos[trim($c)][] = $y;
+	
+	foreach ($s as $x => $c) {
+		$c = trim($c);
+		if (!$t_pos[$c]) continue;
+		krsort($t_pos[$c]);
+		foreach ($t_pos[$c] as $y) {
+			for ($len = $max_len; $len > 0; $len--) {
+				if ($paths_ymin[$len] < $y) {
+					$paths_ymin[$len + 1] = $y;
+					$paths[$len + 1] = $paths[$len];
+					$paths[$len + 1][0][$x] = $y;
+					$paths[$len + 1][1][$y] = $x;
+					break;
+				}
+			}
+			if ($len + 1 > $max_len) $max_len = $len + 1;
+			if ($len == 0) {
+				$paths_ymin[1] = $y;
+				$paths[1] = array(0 => array($x => $y), 1 => array($y => $x));
+			}
+		}
+	}
+	if ($paths[$max_len]) return $paths[$max_len];
+	return array(0 => array(), 1 => array());
+}
+
+
+function test_lcs($a, $b) {
+	$s = explode(" ", $a);
+	$t = explode(" ", $b);
+	
+	$t0 = explode(" ", microtime());
+	list($r1, $r2) = lcs($s, $t);
+	$t1 = explode(" ", microtime());
+	$dt = $t1[0] + $t1[1] - $t0[0] - $t0[1];
+	echo join(" ", $r1)."<br />";
+	echo join(" ", $r2)."<p>";
+	echo "<div style='font-weight: bold; color: red;'>$dt s.</div>";
+}
+
+function test_lcs_opt($s) {
+	$s = preg_split(',\s+,', $s);
+
+	$t0 = explode(" ", microtime());
+	$t = lcs_opt($s);
+	$t1 = explode(" ", microtime());
+	$dt = $t1[0] + $t1[1] - $t0[0] - $t0[1];
+	echo join(" ", $s)."<br />";
+	echo join(" ", $t)."<p>";
+	echo "<div style='font-weight: bold; color: red;'>$dt s.</div>";
+}
+
+
+//
+// Generation de diff a plusieurs etages
+//
+
+class Diff {
+	var $diff;
+	var $fuzzy;
+
+	function Diff($diff) {
+		$this->diff = $diff;
+		$this->fuzzy = true;
+	}
+
+	function comparer($new, $old) {
+		$paras = $this->diff->segmenter($new);
+		$paras_old = $this->diff->segmenter($old);
+		if ($this->diff->fuzzy()) {
+			list($trans_rev, $trans) = apparier_paras($paras_old, $paras);
+			$lcs = lcs_opt($trans);
+			$lcs_rev = array_flip($lcs);
+		}
+		else {
+			list($trans_rev, $trans) = lcs($paras_old, $paras);
+			$lcs = $trans;
+			$lcs_rev = $trans_rev;
+		}
+	
+		reset($paras_old);
+		reset($paras);
+		reset($lcs);
+		unset($i_old);
+		$fin_old = false;
+		foreach ($paras as $i => $p) {
+			if (!isset($trans[$i])) {
+				// Paragraphe ajoute
+				$this->diff->ajouter($p);
+				continue;
+			}
+			$j = $trans[$i];
+			if (!isset($lcs[$i])) {
+				// Paragraphe deplace
+				$this->diff->deplacer($p, $paras_old[$j]);
+				continue;
+			}
+			if (!$fin_old) {
+				// Paragraphes supprimes jusqu'au paragraphe courant
+				if (!isset($i_old)) {
+					list($i_old, $p_old) = each($paras_old);
+					if (!$p_old) $fin_old = true;
+				}
+				while (!$fin_old && $i_old < $j) {
+					if (!isset($trans_rev[$i_old])) {
+						$this->diff->supprimer($p_old);
+					}
+					unset($i_old);
+					list($i_old, $p_old) = each($paras_old);
+					if (!$p_old) $fin_old = true;
+				}
+			}
+			// Paragraphe n'ayant pas change de place
+			$this->diff->comparer($p, $paras_old[$j]);
+		}
+		// Paragraphes supprimes a la fin du texte
+		if (!$fin_old) {
+			if (!isset($i_old)) {
+				list($i_old, $p_old) = each($paras_old);
+				if (!strlen($p_old)) $fin_old = true;
+			}
+			while (!$fin_old) {
+				if (!isset($trans_rev[$i_old])) {
+					$this->diff->supprimer($p_old);
+				}
+				list($i_old, $p_old) = each($paras_old);
+				if (!$p_old) $fin_old = true;
+			}
+		}
+		if (isset($i_old)) {
+			if (!isset($trans_rev[$i_old])) {
+				$this->diff->supprimer($p_old);
+			}
+		}
+		return $this->diff->resultat();
+	}
+}
+
+class DiffTexte {
+	var $r;
+
+	function DiffTexte() {
+		$this->r = "";
+	}
+
+	function _diff($p, $p_old) {
+		$diff = new Diff(new DiffPara);
+		return $diff->comparer($p, $p_old);
+	}
+
+	function fuzzy() {
+		return true;
+	}
+	function segmenter($texte) {
+		return separer_paras($texte);
+	}
+
+	function ajouter($p) {
+		$this->r .= "\n\n\n<div class=\"diff-para-ajoute\" title=\""._L('Paragraphe ajout&eacute;')."\">".$p."</div>";
+	}
+	function supprimer($p_old) {
+		$this->r .= "\n\n\n<div class=\"diff-para-supprime\" title=\""._L('Paragraphe supprim&eacute;')."\">".$p_old."</div>";
+	}
+	function deplacer($p, $p_old) {
+		$this->r .= "\n\n\n<div class=\"diff-para-deplace\" title=\""._L('Paragraphe d&eacute;plac&eacute;')."\">";
+		$this->r .= $this->_diff($p, $p_old);
+		$this->r .= "</div>";
+	}
+	function comparer($p, $p_old) {
+		$this->r .= "\n\n\n".$this->_diff($p, $p_old);
+	}
+	
+	function resultat() {
+		return $this->r;
+	}
+}
+
+class DiffPara {
+	var $r;
+
+	function DiffPara() {
+		$this->r = "";
+	}
+
+	function _diff($p, $p_old) {
+		$diff = new Diff(new DiffPhrase);
+		return $diff->comparer($p, $p_old);
+	}
+
+	function fuzzy() {
+		return true;
+	}
+	function segmenter($texte) {
+		$paras = array();
+		$texte = trim($texte);
+		while (preg_match('/[\.!\?]+\s*/u', $texte, $regs)) {
+			$p = strpos($texte, $regs[0]) + strlen($regs[0]);
+			$paras[] = substr($texte, 0, $p);
+			$texte = substr($texte, $p);
+		}
+		if ($texte) $paras[] = $texte;
+		return $paras;
+	}
+
+	function ajouter($p) {
+		$this->r .= "<span class=\"diff-ajoute\" title=\""._L('Texte ajout&eacute;')."\">".$p."</span>";
+	}
+	function supprimer($p_old) {
+		$this->r .= "<span class=\"diff-supprime\" title=\""._L('Texte supprim&eacute;')."\">".$p_old."</span>";
+	}
+	function deplacer($p, $p_old) {
+		$this->r .= "<span class=\"diff-deplace\" title=\""._L('Texte d&eacute;plac&eacute;')."\">".$this->_diff($p, $p_old)."</span>";
+	}
+	function comparer($p, $p_old) {
+		$this->r .= $this->_diff($p, $p_old);
+	}
+	
+	function resultat() {
+		return $this->r;
+	}
+}
+
+class DiffPhrase {
+	var $r;
+
+	function DiffPhrase() {
+		$this->r = "";
+	}
+
+	function fuzzy() {
+		return false;
+	}
+	function segmenter($texte) {
+		$paras = array();
+		//$texte = trim($texte);
+		while (preg_match('/([[:punct:]]+)(\s+|$)|(\s+)([[:punct:]]*)/u', $texte, $regs)) {
+			$p = strpos($texte, $regs[0]);
+			$l = strlen($regs[0]);
+			$punct = $regs[1] ? $regs[1] : $regs[4];
+			$milieu = "";
+			if ($punct) {
+				// Attacher les raccourcis fermants au mot precedent
+				if (preg_match(',^[\]}]+$,', $punct)) {
+					$avant = substr($texte, 0, $p) . $regs[3] . $punct;
+					$texte = $regs[2] . substr($texte, $p + $l);
+				}
+				// Attacher les raccourcis ouvrants au mot suivant
+				else if ($regs[3] && preg_match(',^[\[{]+$,', $punct)) {
+					$avant = substr($texte, 0, $p) . $regs[3];
+					$texte = $punct . substr($texte, $p + $l);
+				}
+				// Les autres signes de ponctuation sont des mots a part entiere
+				else {
+					$avant = substr($texte, 0, $p);
+					$milieu = $regs[0];
+					$texte = substr($texte, $p + $l);
+				}
+			}
+			else {
+				$avant = substr($texte, 0, $p + $l);
+				$texte = substr($texte, $p + $l);
+			}
+			if ($avant) $paras[] = $avant;
+			if ($milieu) $paras[] = $milieu;
+		}
+		if ($texte) $paras[] = $texte;
+		return $paras;
+	}
+
+	function ajouter($p) {
+		$this->r .= "<span class=\"diff-ajoute\" title=\""._L('Texte ajout&eacute;')."\">".$p."</span> ";
+	}
+	function supprimer($p_old) {
+		$this->r .= "<span class=\"diff-supprime\" title=\""._L('Texte supprim&eacute;')."\">".$p_old."</span> ";
+	}
+	function comparer($p, $p_old) {
+		$this->r .= $p;
+	}
+
+	function resultat() {
+		return $this->r;
+	}
+}
+
+
+function preparer_diff($texte) {
+	include_ecrire("inc_charsets.php3");
+
+	$charset = lire_meta('charset');
+	if ($charset == 'utf-8')
+		return unicode_to_utf_8(html2unicode($texte));
+	return unicode_to_utf_8(html2unicode(charset2unicode($texte, $charset, true)));
+}
+
+function afficher_diff($texte) {
+	$charset = lire_meta('charset');
+	if ($charset == 'utf-8') return $texte;
+	return charset2unicode($texte, 'utf-8');
+}
+
+
+?>
-- 
GitLab