[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / plugins-dist / revisions / inc / revisions.php
index c5aec10..6693ad8 100644 (file)
 /**
  * Fonctions utilitaires du plugin révisions
  *
- * @package Revisions
-**/
-if (!defined("_ECRIRE_INC_VERSION")) return;
+ * @package SPIP\Revisions\Fonctions
+ **/
+if (!defined("_ECRIRE_INC_VERSION")) {
+       return;
+}
 
 $GLOBALS['agregation_versions'] = 10;
 
 /** Intervalle de temps (en seconde) separant deux révisions par un même auteur */
-define('_INTERVALLE_REVISIONS', 600); 
+define('_INTERVALLE_REVISIONS', 600);
 
-// https://code.spip.net/@separer_paras
-function separer_paras($texte, $paras = "") {
-       if (!$paras) $paras = array();
+/**
+ * Découper les paragraphes d'un texte en fragments
+ *
+ * @param string $texte Texte à fragmenter
+ * @param array $paras Tableau de fragments déjà là
+ * @return string[]      Tableau de fragments (paragraphes)
+ **/
+function separer_paras($texte, $paras = array()) {
+       if (!$paras) {
+               $paras = array();
+       }
        while (preg_match("/(\r\n?){2,}|\n{2,}/", $texte, $regs)) {
                $p = strpos($texte, $regs[0]) + strlen($regs[0]);
                $paras[] = substr($texte, 0, $p);
                $texte = substr($texte, $p);
        }
-       if ($texte) $paras[] = $texte;
+       if ($texte) {
+               $paras[] = $texte;
+       }
+
        return $paras;
 }
 
 // https://code.spip.net/@replace_fragment
-function replace_fragment($id_objet,$objet, $version_min, $version_max, $id_fragment, $fragment) {
+function replace_fragment($id_objet, $objet, $version_min, $version_max, $id_fragment, $fragment) {
        $fragment = serialize($fragment);
        $compress = 0;
 
+       /* On ne compresse plus les fragments car probleme de portabilite de base, corruptions de donnees
+       en backup SQLite ou meme en mysqldump */
        // pour le portage en PG il faut l'equivalente au mysql_escape_string
        // et deporter son appel dans les fonctions d'abstraction.
+       /*
        if (function_exists('gzcompress')
        AND $GLOBALS['connexions'][0]['type'] == 'mysql') {
                $s = gzcompress($fragment);
                if (strlen($s) < strlen($fragment)) {
-                       # spip_log("gain gz: ".intval(100 - 100 * strlen($s) / strlen($fragment)));
+                       # spip_log("gain gz: ".intval(100 - 100 * strlen($s) / strlen($fragment)),'revisions');
                        $compress = 1;
                        $fragment = $s;
                }
        }
+       */
 
        // Attention a echapper $fragment, binaire potentiellement gz
        return array(
-                    'id_objet' => intval($id_objet),
-                        'objet' => $objet,
-                    'id_fragment' => intval($id_fragment),
-                    'version_min' => intval($version_min),
-                    'version_max' => intval($version_max),
-                    'compress' => $compress,
-                    'fragment' => $fragment);
+               'id_objet' => intval($id_objet),
+               'objet' => $objet,
+               'id_fragment' => intval($id_fragment),
+               'version_min' => intval($version_min),
+               'version_max' => intval($version_max),
+               'compress' => $compress,
+               'fragment' => $fragment
+       );
 }
 
 // https://code.spip.net/@envoi_replace_fragments
 function envoi_replace_fragments($replaces) {
        $desc = $GLOBALS['tables_auxiliaires']['spip_versions_fragments'];
-       foreach($replaces as $r)
+       foreach ($replaces as $r) {
                sql_replace('spip_versions_fragments', $r, $desc);
+       }
 }
 
 
 // https://code.spip.net/@envoi_delete_fragments
-function envoi_delete_fragments($id_objet,$objet, $deletes) {
+function envoi_delete_fragments($id_objet, $objet, $deletes) {
        if (count($deletes)) {
-               sql_delete("spip_versions_fragments", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND ((".  join(") OR (", $deletes)."))");
+               sql_delete("spip_versions_fragments",
+                       "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND ((" . join(") OR (",
+                               $deletes) . "))");
        }
 }
 
@@ -82,19 +103,23 @@ function envoi_delete_fragments($id_objet,$objet, $deletes) {
 // Ajouter les fragments de la derniere version (tableau associatif id_fragment => texte)
 //
 // https://code.spip.net/@ajouter_fragments
-function ajouter_fragments($id_objet,$objet, $id_version, $fragments) {
+function ajouter_fragments($id_objet, $objet, $id_version, $fragments) {
        global $agregation_versions;
 
        $replaces = array();
        foreach ($fragments as $id_fragment => $texte) {
                $nouveau = true;
                // Recuperer la version la plus recente
-               $row = sql_fetsel("compress, fragment, version_min, version_max", "spip_versions_fragments", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_fragment=$id_fragment AND version_min<=$id_version", "", "version_min DESC", "1");
+               $row = sql_fetsel("compress, fragment, version_min, version_max", "spip_versions_fragments",
+                       "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_fragment=$id_fragment AND version_min<=$id_version",
+                       "", "version_min DESC", "1");
 
                if ($row) {
                        $fragment = $row['fragment'];
                        $version_min = $row['version_min'];
-                       if ($row['compress'] > 0) $fragment = @gzuncompress($fragment);
+                       if ($row['compress'] > 0) {
+                               $fragment = @gzuncompress($fragment);
+                       }
                        $fragment = unserialize($fragment);
                        if (is_array($fragment)) {
                                unset($fragment[$id_version]);
@@ -106,8 +131,7 @@ function ajouter_fragments($id_objet,$objet, $id_version, $fragments) {
                if ($nouveau) {
                        $fragment = array($id_version => $texte);
                        $version_min = $id_version;
-               }
-               else {
+               } else {
                        // Ne pas dupliquer les fragments non modifies
                        $modif = true;
                        for ($i = $id_version - 1; $i >= $version_min; $i--) {
@@ -116,11 +140,13 @@ function ajouter_fragments($id_objet,$objet, $id_version, $fragments) {
                                        break;
                                }
                        }
-                       if ($modif) $fragment[$id_version] = $texte;
+                       if ($modif) {
+                               $fragment[$id_version] = $texte;
+                       }
                }
 
                // Preparer l'enregistrement du fragment
-               $replaces[] = replace_fragment($id_objet,$objet, $version_min, $id_version, $id_fragment, $fragment);
+               $replaces[] = replace_fragment($id_objet, $objet, $version_min, $id_version, $id_fragment, $fragment);
        }
 
        envoi_replace_fragments($replaces);
@@ -131,38 +157,44 @@ function ajouter_fragments($id_objet,$objet, $id_version, $fragments) {
 // (essaie d'eviter une trop grande fragmentation)
 //
 // https://code.spip.net/@supprimer_fragments
-function supprimer_fragments($id_objet,$objet, $version_debut, $version_fin) {
+function supprimer_fragments($id_objet, $objet, $version_debut, $version_fin) {
        global $agregation_versions;
 
        $replaces = array();
        $deletes = array();
 
        // D'abord, vider les fragments inutiles
-       sql_delete("spip_versions_fragments", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND version_min>=$version_debut AND version_max<=$version_fin");
+       sql_delete("spip_versions_fragments",
+               "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND version_min>=$version_debut AND version_max<=$version_fin");
 
        // Fragments chevauchant l'ensemble de l'intervalle, s'ils existent
-       $result = sql_select("id_fragment, compress, fragment, version_min, version_max", "spip_versions_fragments", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND version_min<$version_debut AND version_max>$version_fin");
+       $result = sql_select("id_fragment, compress, fragment, version_min, version_max", "spip_versions_fragments",
+               "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND version_min<$version_debut AND version_max>$version_fin");
 
        while ($row = sql_fetch($result)) {
                $id_fragment = $row['id_fragment'];
                $fragment = $row['fragment'];
-               if ($row['compress'] > 0) $fragment = gzuncompress($fragment);
+               if ($row['compress'] > 0) {
+                       $fragment = gzuncompress($fragment);
+               }
                $fragment = unserialize($fragment);
                for ($i = $version_fin; $i >= $version_debut; $i--) {
                        if (isset($fragment[$i])) {
                                // Recopier le dernier fragment si implicite
-                               if (!isset($fragment[$version_fin + 1]))
+                               if (!isset($fragment[$version_fin + 1])) {
                                        $fragment[$version_fin + 1] = $fragment[$i];
+                               }
                                unset($fragment[$i]);
                        }
                }
 
-               $replaces[] = replace_fragment($id_objet,$objet,
+               $replaces[] = replace_fragment($id_objet, $objet,
                        $row['version_min'], $row['version_max'], $id_fragment, $fragment);
        }
 
        // Fragments chevauchant le debut de l'intervalle, s'ils existent
-       $result = sql_select("id_fragment, compress, fragment, version_min, version_max", "spip_versions_fragments", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND version_min<$version_debut AND version_max>=$version_debut AND version_max<=$version_fin");
+       $result = sql_select("id_fragment, compress, fragment, version_min, version_max", "spip_versions_fragments",
+               "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND version_min<$version_debut AND version_max>=$version_debut AND version_max<=$version_fin");
 
        $deb_fragment = array();
        while ($row = sql_fetch($result)) {
@@ -170,10 +202,14 @@ function supprimer_fragments($id_objet,$objet, $version_debut, $version_fin) {
                $fragment = $row['fragment'];
                $version_min = $row['version_min'];
                $version_max = $row['version_max'];
-               if ($row['compress'] > 0) $fragment = gzuncompress($fragment);
+               if ($row['compress'] > 0) {
+                       $fragment = gzuncompress($fragment);
+               }
                $fragment = unserialize($fragment);
                for ($i = $version_debut; $i <= $version_max; $i++) {
-                       if (isset($fragment[$i])) unset($fragment[$i]);
+                       if (isset($fragment[$i])) {
+                               unset($fragment[$i]);
+                       }
                }
 
                // Stocker temporairement le fragment pour agregation
@@ -184,20 +220,24 @@ function supprimer_fragments($id_objet,$objet, $version_debut, $version_fin) {
        }
 
        // Fragments chevauchant la fin de l'intervalle, s'ils existent
-       $result = sql_select("id_fragment, compress, fragment, version_min, version_max", "spip_versions_fragments", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND version_max>$version_fin AND version_min>=$version_debut AND version_min<=$version_fin");
+       $result = sql_select("id_fragment, compress, fragment, version_min, version_max", "spip_versions_fragments",
+               "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND version_max>$version_fin AND version_min>=$version_debut AND version_min<=$version_fin");
 
        while ($row = sql_fetch($result)) {
                $id_fragment = $row['id_fragment'];
                $fragment = $row['fragment'];
                $version_min = $row['version_min'];
                $version_max = $row['version_max'];
-               if ($row['compress'] > 0) $fragment = gzuncompress($fragment);
+               if ($row['compress'] > 0) {
+                       $fragment = gzuncompress($fragment);
+               }
                $fragment = unserialize($fragment);
                for ($i = $version_fin; $i >= $version_min; $i--) {
                        if (isset($fragment[$i])) {
                                // Recopier le dernier fragment si implicite
-                               if (!isset($fragment[$version_fin + 1]))
+                               if (!isset($fragment[$version_fin + 1])) {
                                        $fragment[$version_fin + 1] = $fragment[$i];
+                               }
                                unset($fragment[$i]);
                        }
                }
@@ -211,9 +251,8 @@ function supprimer_fragments($id_objet,$objet, $version_debut, $version_fin) {
                        if ($agreger) {
                                $fragment = $deb_fragment[$id_fragment] + $fragment;
                                $version_min = $deb_version_min[$id_fragment];
-                       }
-                       else {
-                               $replaces[] = replace_fragment($id_objet,$objet,
+                       } else {
+                               $replaces[] = replace_fragment($id_objet, $objet,
                                        $deb_version_min[$id_fragment], $deb_version_max[$id_fragment],
                                        $id_fragment, $deb_fragment[$id_fragment]);
                        }
@@ -229,44 +268,59 @@ function supprimer_fragments($id_objet,$objet, $version_debut, $version_fin) {
        // Ajouter fragments restants
        if (is_array($deb_fragment) && count($deb_fragment) > 0) {
                foreach ($deb_fragment as $id_fragment => $fragment) {
-                       $replaces[] = replace_fragment($id_objet,$objet,
+                       $replaces[] = replace_fragment($id_objet, $objet,
                                $deb_version_min[$id_fragment], $deb_version_max[$id_fragment],
                                $id_fragment, $deb_fragment[$id_fragment]);
                }
        }
 
        envoi_replace_fragments($replaces);
-       envoi_delete_fragments($id_objet,$objet, $deletes);
+       envoi_delete_fragments($id_objet, $objet, $deletes);
 }
 
-//
-// Recuperer les fragments d'une version donnee
-// renvoie un tableau associatif (id_fragment => texte)
-//
-// https://code.spip.net/@recuperer_fragments
-function recuperer_fragments($id_objet,$objet, $id_version) {
+
+/**
+ * Récupérer les fragments d'un objet pour une version demandée
+ *
+ * @param int $id_objet Identifiant de l'objet
+ * @param string $objet Objet
+ * @param int $id_version Identifiant de la version
+ * @return array           Couples id_fragment => texte
+ */
+function recuperer_fragments($id_objet, $objet, $id_version) {
        $fragments = array();
 
-       if ($id_version == 0) return array();
+       if ($id_version == 0) {
+               return array();
+       }
 
-       $result = sql_select("id_fragment, version_min, version_max, compress, fragment", "spip_versions_fragments", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND version_min<=$id_version AND version_max>=$id_version");
+       $result = sql_select(
+               "id_fragment, version_min, version_max, compress, fragment",
+               "spip_versions_fragments",
+               "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet)
+               . " AND version_min<=$id_version AND version_max>=$id_version");
 
        while ($row = sql_fetch($result)) {
                $id_fragment = $row['id_fragment'];
                $version_min = $row['version_min'];
                $fragment = $row['fragment'];
-               if ($row['compress'] > 0){
+               // si le fragment est compressé, tenter de le décompresser, sinon écrire une erreur
+               if ($row['compress'] > 0) {
                        $fragment_ = @gzuncompress($fragment);
-                       if (strlen($fragment) && $fragment_===false)
-                               $fragment=serialize(array($row['version_max']=>"["._T('forum_titre_erreur').$id_fragment."]"));
-                       else
-                        $fragment = $fragment_;
+                       if (strlen($fragment) && $fragment_ === false) {
+                               $fragment = serialize(array($row['version_max'] => "[" . _T('forum_titre_erreur') . $id_fragment . "]"));
+                       } else {
+                               $fragment = $fragment_;
+                       }
                }
+               // tenter dedésérialiser le fragment, sinon écrire une erreur
                $fragment_ = unserialize($fragment);
-               if (strlen($fragment) && $fragment_===false)
-                       $fragment=array($row['version_max']=>"["._T('forum_titre_erreur').$id_fragment."]");
-               else
-                $fragment = $fragment_;
+               if (strlen($fragment) && $fragment_ === false) {
+                       $fragment = array($row['version_max'] => "[" . _T('forum_titre_erreur') . $id_fragment . "]");
+               } else {
+                       $fragment = $fragment_;
+               }
+               // on retrouve le fragment le plus près de notre version
                for ($i = $id_version; $i >= $version_min; $i--) {
                        if (isset($fragment[$i])) {
 
@@ -275,16 +329,19 @@ function recuperer_fragments($id_objet,$objet, $id_version) {
                                ## mais ce code va les nettoyer ; pour les autres charsets
                                ## la situation n'est pas meilleure ni pire qu'avant)
                                if ($GLOBALS['meta']['charset'] == 'utf-8'
-                               AND include_spip('inc/charsets')
-                               AND !is_utf8($fragment[$i])) {
+                                       and include_spip('inc/charsets')
+                                       and !is_utf8($fragment[$i])
+                               ) {
                                        $fragment[$i] = importer_charset($fragment[$i], 'iso-8859-1');
                                }
 
                                $fragments[$id_fragment] = $fragment[$i];
+                               // quitter la boucle dès le premier touvé.
                                break;
                        }
                }
        }
+
        return $fragments;
 }
 
@@ -306,20 +363,24 @@ function apparier_paras($src, $dest, $flou = true) {
        $l1 = $l2 = array();
 
        // Nettoyage de la ponctuation pour faciliter l'appariement
-       foreach($src as $key => $val) {
+       foreach ($src as $key => $val) {
                $t1[$key] = strval(preg_replace("/[[:punct:][:space:]]+/", " ", $val));
        }
-       foreach($dest as $key => $val) {
+       foreach ($dest as $key => $val) {
                $t2[$key] = strval(preg_replace("/[[:punct:][:space:]]+/", " ", $val));
        }
 
        // Premiere passe : chercher les correspondance exactes
-       foreach($t1 as $key => $val) $md1[$key] = md5($val);
-       foreach($t2 as $key => $val) $md2[md5($val)][$key] = $key;
-       foreach($md1 as $key1 => $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 (isset($md2[$h])) {
                        $key2 = reset($md2[$h]);
-                       if (isset($t1[$key1]) AND isset($t2[$key2]) AND $t1[$key1] == $t2[$key2]) {
+                       if (isset($t1[$key1]) and isset($t2[$key2]) and $t1[$key1] == $t2[$key2]) {
                                $src_dest[$key1] = $key2;
                                $dest_src[$key2] = $key1;
                                unset($t1[$key1]);
@@ -331,21 +392,21 @@ function apparier_paras($src, $dest, $flou = true) {
 
        if ($flou) {
                // Deuxieme passe : recherche de correlation par test de compressibilite
-               foreach($t1 as $key => $val) {
+               foreach ($t1 as $key => $val) {
                        $l1[$key] = strlen(gzcompress($val));
                }
-               foreach($t2 as $key => $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));
+               foreach ($t1 as $key1 => $s1) {
+                       foreach ($t2 as $key2 => $s2) {
+                               $r = strlen(gzcompress($s1 . $s2));
                                $taux = 1.0 * $r / ($l1[$key1] + $l2[$key2]);
                                if (!isset($gz_min1[$key1]) || !$gz_min1[$key1] || $gz_min1[$key1] > $taux) {
                                        $gz_min1[$key1] = $taux;
                                        $gz_trans1[$key1] = $key2;
                                }
-                               if (!isset($gz_min2[$key2]) ||!$gz_min2[$key2] || $gz_min2[$key2] > $taux) {
+                               if (!isset($gz_min2[$key2]) || !$gz_min2[$key2] || $gz_min2[$key2] > $taux) {
                                        $gz_min2[$key2] = $taux;
                                        $gz_trans2[$key2] = $key1;
                                }
@@ -354,7 +415,7 @@ function apparier_paras($src, $dest, $flou = true) {
 
                // Depouiller les resultats de la deuxieme passe :
                // ne retenir que les correlations reciproques
-               foreach($gz_trans1 as $key1 => $key2) {
+               foreach ($gz_trans1 as $key1 => $key2) {
                        if ($gz_trans2[$key2] == $key1 && $gz_min1[$key1] < 0.9) {
                                $src_dest[$key1] = $key2;
                                $dest_src[$key2] = $key1;
@@ -366,24 +427,77 @@ function apparier_paras($src, $dest, $flou = true) {
        return array($src_dest, $dest_src);
 }
 
-//
-// Recuperer les champs d'une version donnee
-//
-// https://code.spip.net/@recuperer_version
-function recuperer_version($id_objet,$objet, $id_version) {
+/**
+ * Retrouve les champs d'un objet et leurs contenus à une version donnée
+ *
+ * @uses recuperer_version()
+ * @uses liste_champs_versionnes()
+ * @uses retrouver_champ_version_objet()
+ * 
+ * @param int $id_objet Identifiant de l'objet
+ * @param string $objet Objet
+ * @param int $id_version Identifiant de la version
+ * @return array           Couples champs => textes 
+**/
+function recuperer_version_complete($id_objet, $objet, $id_version) {
+       if (!$id_version or !$id_objet or !$objet) {
+               return array();
+       }
+
+       include_spip('inc/suivi_versions');
+
+       // champs modifiés à la version voulue
+       $textes = recuperer_version($id_objet, $objet, $id_version);
+
+       // tous les champs possibles versionnés pour l'objet
+       $champs = liste_champs_versionnes(table_objet_sql($objet));
+
+       foreach ($champs as $champ) {
+               // Remonter dans le temps pour trouver le champ en question pour chaque version
+               retrouver_champ_version_objet($objet, $id_objet, $id_version, $champ, $textes);
+       }
+
+       return $textes;
+}
+
+/**
+ * Récupérer les champs d'un objet, pour une version demandée
+ *
+ * @param int $id_objet Identifiant de l'objet
+ * @param string $objet Objet
+ * @param int $id_version Identifiant de la version
+ * @return array           Couples champs => textes
+ */
+function recuperer_version($id_objet, $objet, $id_version) {
 
-       $champs = sql_getfetsel("champs", "spip_versions", "id_objet=" . intval($id_objet) . " AND objet=".sql_quote($objet)." AND id_version=" . intval($id_version));
-       if (!$champs OR !is_array($champs = unserialize($champs)))
+       $champs = sql_getfetsel("champs", "spip_versions",
+               "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version=" . intval($id_version));
+       if (!$champs or !is_array($champs = unserialize($champs))) {
                return array();
-       else return reconstuire_version($champs,
-                        recuperer_fragments($id_objet,$objet, $id_version));
+       } else {
+               return reconstuire_version($champs, 
+                       recuperer_fragments($id_objet, $objet, $id_version));
+       }
 }
 
-// https://code.spip.net/@reconstuire_version
-function reconstuire_version($champs, $fragments, $res=array()) {
+/**
+ * Reconstruire une version donnée
+ *
+ * À partir de la liste des champs et de fragments,
+ * retourne le texte de chaque champ.
+ *
+ * @param array $champs Couples (champ => liste d'id_fragment).
+ *                          La liste est de la forme "5 32 7 16 8 2"
+ * @param array $fragments Couples (id_fragment => texte)
+ * @param array $res Couples (champ => texte) déjà connus
+ * @return array            Couples (champ => texte)
+ */
+function reconstuire_version($champs, $fragments, $res = array()) {
 
        static $msg;
-       if (!$msg) $msg = _T('forum_titre_erreur');
+       if (!$msg) {
+               $msg = _T('forum_titre_erreur');
+       }
 
        foreach ($champs as $nom => $code) {
                if (!isset($res[$nom])) {
@@ -396,48 +510,70 @@ function reconstuire_version($champs, $fragments, $res=array()) {
                        $res[$nom] = $t;
                }
        }
+
        return $res;
 }
 
 // https://code.spip.net/@supprimer_versions
-function supprimer_versions($id_objet,$objet, $version_min, $version_max) {
-       sql_delete("spip_versions", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version>=$version_min AND id_version<=$version_max");
+function supprimer_versions($id_objet, $objet, $version_min, $version_max) {
+       sql_delete("spip_versions",
+               "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version>=$version_min AND id_version<=$version_max");
 
-       supprimer_fragments($id_objet,$objet, $version_min, $version_max);
+       supprimer_fragments($id_objet, $objet, $version_min, $version_max);
 }
 
-//
-// Ajouter une version a un objet
-//
-// https://code.spip.net/@ajouter_version
-function ajouter_version($id_objet,$objet, $champs, $titre_version = "", $id_auteur) {
+
+/**
+ * Ajouter une version à un objet éditorial
+ *
+ * @param int $id_objet
+ * @param string $objet
+ * @param array $champs
+ * @param string $titre_version
+ *     Titre donné aux modifications apportées
+ * @param int|null $id_auteur
+ *     Auteur apportant les modifications. En absence (session anonyme), utilisera l'IP pour garder une trace.
+ * @return int
+ *     id_version : identifiant de la version
+ **/
+function ajouter_version($id_objet, $objet, $champs, $titre_version = "", $id_auteur = null) {
        $paras = $paras_old = $paras_champ = $fragments = array();
 
        // Attention a une edition anonyme (type wiki): id_auteur n'est pas
        // definie, on enregistre alors le numero IP
        $str_auteur = intval($id_auteur) ? intval($id_auteur) : $GLOBALS['ip'];
+
        // si pas de titre dans cette version, la marquer 'non' permanente,
        // et elle pourra etre fusionnee avec une revision ulterieure dans un delai < _INTERVALLE_REVISIONS
        // permet de fusionner plusieurs editions consecutives champs par champs avec les crayons
        $permanent = empty($titre_version) ? 'non' : '';
 
        // Detruire les tentatives d'archivages non abouties en 1 heure
-       sql_delete('spip_versions', "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version <= 0 AND date < DATE_SUB(".sql_quote(date('Y-m-d H:i:s')).", INTERVAL "._INTERVALLE_REVISIONS." SECOND)");
+       sql_delete('spip_versions', array(
+                       "id_objet=" . intval($id_objet),
+                       "objet=" . sql_quote($objet),
+                       "id_version <= 0",
+                       "date < DATE_SUB(" . sql_quote(date('Y-m-d H:i:s')) . ", INTERVAL " . _INTERVALLE_REVISIONS . " SECOND)"
+               )
+       );
 
        // Signaler qu'on opere en mettant un numero de version negatif
        // distinctif (pour eviter la violation d'unicite)
        // et un titre contenant en fait le moment de l'insertion
        list($ms, $sec) = explode(' ', microtime());
-       $date = $sec . substr($ms,1,4); // SQL ne ramene que 4 chiffres significatifs apres la virgule pour 0.0+titre_version
-       $datediff = ($sec - mktime(0,0,0,9,1,2007)) * 1000000 + substr($ms,2, strlen($ms)-4);
-
-       $valeurs = array('id_objet' => $id_objet,
-                        'objet' => $objet,
-                        'id_version' => (0 - $datediff),
-                        'date' => date('Y-m-d H:i:s'),
-                        'id_auteur' => $str_auteur, //  varchar ici!
-                        'titre_version' => $date);
-       sql_insertq('spip_versions',  $valeurs);
+       $date = $sec . substr($ms, 1,
+                       4) - 20; // SQL ne ramene que 4 chiffres significatifs apres la virgule pour 0.0+titre_version
+       $datediff = ($sec - mktime(0, 0, 0, 9, 1, 2007)) * 1000000 + substr($ms, 2, strlen($ms) - 4);
+
+       $valeurs = array(
+               'id_objet' => $id_objet,
+               'objet' => $objet,
+               'id_version' => (0 - $datediff),
+               'date' => date('Y-m-d H:i:s'),
+               'id_auteur' => $str_auteur, //  varchar ici!
+               'titre_version' => $date
+       );
+       sql_insertq('spip_versions', $valeurs);
 
        // Eviter les validations entremelees en s'endormant s'il existe
        // une version <0 plus recente mais pas plus vieille que 10s
@@ -450,28 +586,34 @@ function ajouter_version($id_objet,$objet, $champs, $titre_version = "", $id_aut
        // 3. recommenter le premier sleep(15), decommenter le second.
        // 4. enregistrer une autre modif dans les 15 secondes
 #        sleep(15);
-       $delai = $sec-10;
-       while (sql_countsel('spip_versions', "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version < 0 AND 0.0+titre_version < $date AND titre_version<>".sql_quote($date,'','text')." AND 0.0+titre_version > $delai")) {
-               spip_log("version $objet $id_objet :insertion en cours avant $date ($delai)");
+       $delai = $sec - 10;
+       while (sql_countsel('spip_versions',
+               "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version < 0 AND 0.0+titre_version < $date AND titre_version<>" . sql_quote($date,
+                       '', 'text') . " AND 0.0+titre_version > $delai")) {
+               spip_log("version $objet $id_objet :insertion en cours avant $date ($delai)", 'revisions');
                sleep(1);
                $delai++;
        }
 #   sleep(15);         spip_log("sortie $sec $delai");
        // Determiner le numero du prochain fragment
-       $next = sql_fetsel("id_fragment", "spip_versions_fragments", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet), "", "id_fragment DESC", "1");
+       $next = sql_fetsel("id_fragment", "spip_versions_fragments",
+               "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet), "", "id_fragment DESC", "1");
 
        $onlylock = '';
 
        // Examiner la derniere version
-       $row = sql_fetsel("id_version, champs, id_auteur, date, permanent", "spip_versions", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version > 0", '', "id_version DESC", "1"); // le champ id_auteur est un varchar dans cette table
+       $row = sql_fetsel("id_version, champs, id_auteur, date, permanent", "spip_versions",
+               "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version > 0", '', "id_version DESC",
+               "1"); // le champ id_auteur est un varchar dans cette table
 
        if ($row) {
                $id_version = $row['id_version'];
-               $paras_old = recuperer_fragments($id_objet,$objet, $id_version);
+               $paras_old = recuperer_fragments($id_objet, $objet, $id_version);
                $champs_old = $row['champs'];
-               if ($row['id_auteur']!= $str_auteur
-                 OR $row['permanent']!='non'
-                 OR strtotime($row['date']) < (time()-_INTERVALLE_REVISIONS)) {
+               if ($row['id_auteur'] != $str_auteur
+                       or $row['permanent'] != 'non'
+                       or strtotime($row['date']) < (time() - _INTERVALLE_REVISIONS)
+               ) {
                        spip_log(strtotime($row['date']), 'revisions');
                        spip_log(time(), 'revisions');
                        spip_log(_INTERVALLE_REVISIONS, 'revisions');
@@ -483,12 +625,9 @@ function ajouter_version($id_objet,$objet, $champs, $titre_version = "", $id_aut
                        $champs = reconstuire_version(unserialize($champs_old), $paras_old, $champs);
                        $onlylock = 're';
                }
-       } else
+       } else {
                $id_version = 1;
-
-       spip_log($str_auteur, 'revisions');
-       spip_log($row, 'revisions');
-       spip_log($id_version, 'revisions');
+       }
 
        $next = !$next ? 1 : ($next['id_fragment'] + 1);
 
@@ -504,16 +643,18 @@ function ajouter_version($id_objet,$objet, $champs, $titre_version = "", $id_aut
        $n = count($paras);
        if ($n) {
                // Tables d'appariement dans les deux sens
-               list(,$trans) = apparier_paras($paras_old, $paras);
+               list(, $trans) = apparier_paras($paras_old, $paras);
                reset($champs);
                $nom = '';
 
                // eviter une notice PHP au tout debut de la boucle
                // on ajoute ''=>0 en debut de tableau.
-               $paras_champ = array($nom=>0) + $paras_champ;
+               $paras_champ = array($nom => 0) + $paras_champ;
 
                for ($i = 0; $i < $n; $i++) {
-                       while ($i >= $paras_champ[$nom]) list($nom, ) = each($champs);
+                       while ($i >= $paras_champ[$nom]) {
+                               list($nom, ) = each($champs);
+                       }
                        // Lier au fragment existant si possible, sinon creer un nouveau fragment
                        $id_fragment = isset($trans[$i]) ? $trans[$i] : $next++;
                        $codes[$nom][] = $id_fragment;
@@ -527,20 +668,32 @@ function ajouter_version($id_objet,$objet, $champs, $titre_version = "", $id_aut
        }
 
        // Enregistrer les modifications
-       ajouter_fragments($id_objet,$objet, $id_version, $fragments);
+       ajouter_fragments($id_objet, $objet, $id_version, $fragments);
 
        // Si l'insertion ne servait que de verrou,
        // la detruire apres mise a jour de l'ancienne entree,
        // sinon la mise a jour efface en fait le verrou.
 
        if (!$onlylock) {
-               sql_updateq('spip_versions', array('id_version'=>$id_version, 'date'=>date('Y-m-d H:i:s'), 'champs'=> serialize($codes), 'permanent'=>$permanent, 'titre_version'=> $titre_version), "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version < 0 AND titre_version='$date'");
+               sql_updateq('spip_versions', array(
+                       'id_version' => $id_version,
+                       'date' => date('Y-m-d H:i:s'),
+                       'champs' => serialize($codes),
+                       'permanent' => $permanent,
+                       'titre_version' => $titre_version
+               ),
+                       "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version < 0 AND titre_version='$date'");
        } else {
-               sql_updateq('spip_versions', array('date'=>date('Y-m-d H:i:s'), 'champs'=>serialize($codes), 'permanent'=>$permanent, 'titre_version'=> $titre_version), "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version=$id_version");
-
-               sql_delete("spip_versions", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version < 0 AND titre_version ='$date'");
+               sql_updateq('spip_versions', array(
+                       'date' => date('Y-m-d H:i:s'),
+                       'champs' => serialize($codes),
+                       'permanent' => $permanent,
+                       'titre_version' => $titre_version
+               ), "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version=$id_version");
+               sql_delete("spip_versions",
+                       "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version < 0 AND titre_version ='$date'");
        }
-       spip_log($onlylock . "memorise la version $id_version de l'objet $objet $id_objet $titre_version");
+       spip_log($onlylock . "memorise la version $id_version de l'objet $objet $id_objet $titre_version", 'revisions');
 
        return $id_version;
 }
@@ -552,9 +705,9 @@ function propre_diff($texte) {
 
        $span_diff = array();
        if (preg_match_all(',<(/)?(span|div) (class|rem)="diff-[^>]*>,', $texte, $regs, PREG_SET_ORDER)) {
-               $regs = array_slice($regs,0,500); #limiter la casse s'il y en a trop
+               $regs = array_slice($regs, 0, 500); #limiter la casse s'il y en a trop
                foreach ($regs as $c => $reg) {
-                       $texte = str_replace($reg[0], '@@@SPIP_DIFF'.$c.'@@@', $texte);
+                       $texte = str_replace($reg[0], '@@@SPIP_DIFF' . $c . '@@@', $texte);
                }
        }
 
@@ -587,23 +740,27 @@ function propre_diff($texte) {
        if (preg_match_all(',<textarea (.*)</textarea>,Uims', $texte, $area, PREG_SET_ORDER)) {
                foreach ($area as $reg) {
                        $remplace = preg_replace(',@@@SPIP_DIFF[0-9]+@@@,', '**', $reg[0]);
-                       if ($remplace <> $reg[0])
+                       if ($remplace <> $reg[0]) {
                                $texte = str_replace($reg[0], $remplace, $texte);
+                       }
                }
        }
 
        // replacer les valeurs des <span> et <div> diff-
-       if (is_array($regs))
-       foreach ($regs as $c => $reg) {
-               $bal = (!$reg[1]) ? $reg[0] : "</$reg[2]>";
-               $texte = str_replace('@@@SPIP_DIFF'.$c.'@@@', $bal, $texte);
-               $GLOBALS['les_notes'] = str_replace('@@@SPIP_DIFF'.$c.'@@@', $bal, $GLOBALS['les_notes']);
+       if (is_array($regs)) {
+               foreach ($regs as $c => $reg) {
+                       $bal = (!$reg[1]) ? $reg[0] : "</$reg[2]>";
+                       $texte = str_replace('@@@SPIP_DIFF' . $c . '@@@', $bal, $texte);
+                       $GLOBALS['les_notes'] = str_replace('@@@SPIP_DIFF' . $c . '@@@', $bal, $GLOBALS['les_notes']);
+               }
        }
 
 
        // quand le dernier tag est ouvrant le refermer ...
        $reg = end($regs);
-       if (!$reg[1] AND $reg[2]) $texte.="</$reg[2]>";
+       if (!$reg[1] and $reg[2]) {
+               $texte .= "</$reg[2]>";
+       }
 
        // et interdire_scripts !
        $texte = interdire_scripts($texte);
@@ -614,7 +771,7 @@ function propre_diff($texte) {
 
 /**
  * Liste les champs versionnés d'une table objet.
- * 
+ *
  * @param string $table
  *     Nom complet de sa table sql. Exemple 'spip_articles'
  * @return array
@@ -623,13 +780,16 @@ function propre_diff($texte) {
 function liste_champs_versionnes($table) {
        $liste_objets_versionnees = is_array(unserialize($GLOBALS['meta']['objets_versions'])) ? unserialize($GLOBALS['meta']['objets_versions']) : array();
 
-       if (!in_array($table,$liste_objets_versionnees))
+       if (!in_array($table, $liste_objets_versionnees)) {
                return array();
+       }
 
        include_spip('base/objets');
-       if ($infos=lister_tables_objets_sql($table)
-         AND isset($infos['champs_versionnes']))
+       if ($infos = lister_tables_objets_sql($table)
+               and isset($infos['champs_versionnes'])
+       ) {
                return $infos['champs_versionnes'];
+       }
 
        return array();
 }
@@ -637,39 +797,41 @@ function liste_champs_versionnes($table) {
 /**
  * Lorsqu'un champ versionée est une jointure, récuperer tous les liens
  * et les mettre sous forme de liste énumérée
- * 
+ *
  * @param string $objet
  * @param string $id_objet
  * @param string $jointure
  * @return string
  */
-function recuperer_valeur_champ_jointure($objet,$id_objet,$jointure){
+function recuperer_valeur_champ_jointure($objet, $id_objet, $jointure) {
        $objet_joint = objet_type($jointure);
        include_spip('action/editer_liens');
        $v = array();
        if (objet_associable($objet_joint)) {
-               $liens = objet_trouver_liens(array($objet_joint=>'*'),array($objet=>$id_objet));
-               foreach($liens as $l)
+               $liens = objet_trouver_liens(array($objet_joint => '*'), array($objet => $id_objet));
+               foreach ($liens as $l) {
                        $v[] = $l[$objet_joint];
-       }
-       elseif(objet_associable($objet)) {
-               $liens = objet_trouver_liens(array($objet=>$id_objet),array($objet_joint=>'*'));
-               foreach($liens as $l)
+               }
+       } elseif (objet_associable($objet)) {
+               $liens = objet_trouver_liens(array($objet => $id_objet), array($objet_joint => '*'));
+               foreach ($liens as $l) {
                        $v[] = $l[$objet];
+               }
        }
        sort($v);
-       return implode(",",$v);
+
+       return implode(",", $v);
 }
 
 /**
  * Créer la première révision d'un objet si nécessaire
- * 
+ *
  * À faire notamment si on vient d'activer l'extension et qu'on fait une modif
  * sur un objet qui était déjà en base, mais non versionné
  *
  * La fonction renvoie le numéro de la dernière version de l'objet,
  * et 0 si pas de version pour cet objet
- * 
+ *
  * @param string $table
  * @param string $objet
  * @param int $id_objet
@@ -677,56 +839,67 @@ function recuperer_valeur_champ_jointure($objet,$id_objet,$jointure){
  * @param int $id_auteur
  * @return int
  */
-function verifier_premiere_revision($table,$objet,$id_objet,$champs=null, $id_auteur=0){
+function verifier_premiere_revision($table, $objet, $id_objet, $champs = null, $id_auteur = 0) {
 
        $id_table_objet = id_table_objet($objet);
-       if (!$champs)
+       if (!$champs) {
                $champs = liste_champs_versionnes($table);
-       if (!$champs)
+       }
+       if (!$champs) {
                return false;
+       }
 
-       if (!$id_version = sql_getfetsel('id_version','spip_versions',"id_objet=".intval($id_objet)." AND objet=".sql_quote($objet),'','id_version DESC','0,1')) {
+       if (!$id_version = sql_getfetsel('id_version', 'spip_versions',
+               "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet), '', 'id_version DESC', '0,1')
+       ) {
                // recuperer toutes les valeurs actuelles des champs
                // pour l'objet
-               $originaux = sql_fetsel("*", $table, "$id_table_objet=".intval($id_objet));
+               $originaux = sql_fetsel("*", $table, "$id_table_objet=" . intval($id_objet));
                $premiere = false;
-               foreach($champs as $v){
-                       if (isset($originaux[$v])){
+               $champs_originaux = array();
+
+               foreach ($champs as $v) {
+                       if (isset($originaux[$v])) {
                                $champs_originaux[$v] = $originaux[$v];
+                       } elseif (strncmp($v, 'jointure_', 9) == 0) {
+                               $champs_originaux[$v] = recuperer_valeur_champ_jointure($objet, $id_objet, substr($v, 9));
                        }
-                       else if(strncmp($v,'jointure_',9)==0) {
-                               $champs_originaux[$v] = recuperer_valeur_champ_jointure($objet,$id_objet,substr($v,9));
-                       }
-                       if (isset($champs_originaux[$v]) AND strlen($originaux[$v]))
+                       if (isset($champs_originaux[$v]) and isset($originaux[$v]) and strlen($originaux[$v])) {
                                $premiere = true;
+                       }
                }
 
                // Si un champ est non vide,
                // il faut creer une premiere revision
                if ($premiere) {
-                       $trouver_table = charger_fonction('trouver_table','base');
+                       $trouver_table = charger_fonction('trouver_table', 'base');
                        $desc = $trouver_table($table);
 
                        // "trouver" une date raisonnable pour la version initiale
 
                        $date_modif = "";
-                       foreach(array('date_modif','maj') as $d){
-                               if (!$date_modif AND isset($originaux[$d]) AND $t=strtotime($originaux[$d]))
-                                       $date_modif = date("Y-m-d H:i:s", $t);
+                       foreach (array('date_modif', 'maj') as $d) {
+                               if (!$date_modif and isset($originaux[$d]) and $t = strtotime($originaux[$d])) {
+                                       $date_modif = date("Y-m-d H:i:s", $t - 20);
+                               }
                        }
                        if (!$date_modif
-                         AND isset($desc['date'])
-                         AND isset($originaux[$desc['date']])) {
+                               and isset($desc['date'])
+                               and isset($originaux[$desc['date']])
+                       ) {
                                $date_modif = $originaux[$desc['date']];
+                       } elseif (!$date_modif) {
+                               $date_modif = date("Y-m-d H:i:s", time() - 20);
+                       }
+
+                       if ($id_version = ajouter_version($id_objet, $objet, $champs_originaux, _T('revisions:version_initiale'),
+                               $id_auteur)
+                       ) {
+                               sql_updateq('spip_versions', array('date' => $date_modif),
+                                       "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version=$id_version");
                        }
-                       elseif (!$date_modif)
-                               $date_modif = date("Y-m-d H:i:s", time()-7200);
-                       
-                       if ($id_version = ajouter_version($id_objet, $objet, $champs_originaux, _T('revisions:version_initiale'), $id_auteur))
-                               sql_updateq('spip_versions', array('date' => $date_modif), "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version=$id_version");
                }
        }
+
        return $id_version;
 }
-
-?>