source: spip-zone/_core_/branches/spip-3.0/plugins/revisions/inc/revisions.php @ 67235

Last change on this file since 67235 was 67235, checked in by cedric@…, 7 years ago

Ajouter un DEFAULT sur 'permanent' et 'champs' car peut poser problème sur certains sites SQLite migrés depuis mySQL (qui ont attrapés au passage une clause NOT NULL)

Gerer le 'permanent' dans le sens inverse : on tag 'non' quand c'est une revision fusionnable, et rien sinon (le cas le plus courant), et on teste l'absence de 'non' pour forcer la creation d'une nouvelle revision. Cela évite de peupler la base pour rien, et evite des bugs après réparation de base sur le permanent problematique

File size: 25.6 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2011                                                *
7 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8 *                                                                         *
9 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11\***************************************************************************/
12
13/**
14 * Fonctions utilitaires du plugin révisions
15 *
16 * @package Revisions
17**/
18if (!defined("_ECRIRE_INC_VERSION")) return;
19
20$GLOBALS['agregation_versions'] = 10;
21
22/** Intervalle de temps (en seconde) separant deux révisions par un même auteur */
23define('_INTERVALLE_REVISIONS', 600); 
24
25// http://doc.spip.org/@separer_paras
26function separer_paras($texte, $paras = "") {
27        if (!$paras) $paras = array();
28        while (preg_match("/(\r\n?){2,}|\n{2,}/", $texte, $regs)) {
29                $p = strpos($texte, $regs[0]) + strlen($regs[0]);
30                $paras[] = substr($texte, 0, $p);
31                $texte = substr($texte, $p);
32        }
33        if ($texte) $paras[] = $texte;
34        return $paras;
35}
36
37// http://doc.spip.org/@replace_fragment
38function replace_fragment($id_objet,$objet, $version_min, $version_max, $id_fragment, $fragment) {
39        $fragment = serialize($fragment);
40        $compress = 0;
41
42        // pour le portage en PG il faut l'equivalente au mysql_escape_string
43        // et deporter son appel dans les fonctions d'abstraction.
44        if (function_exists('gzcompress')
45        AND $GLOBALS['connexions'][0]['type'] == 'mysql') {
46                $s = gzcompress($fragment);
47                if (strlen($s) < strlen($fragment)) {
48                        # spip_log("gain gz: ".intval(100 - 100 * strlen($s) / strlen($fragment)));
49                        $compress = 1;
50                        $fragment = $s;
51                }
52        }
53
54        // Attention a echapper $fragment, binaire potentiellement gz
55        return array(
56                     'id_objet' => intval($id_objet),
57                         'objet' => $objet,
58                     'id_fragment' => intval($id_fragment),
59                     'version_min' => intval($version_min),
60                     'version_max' => intval($version_max),
61                     'compress' => $compress,
62                     'fragment' => $fragment);
63}
64
65// http://doc.spip.org/@envoi_replace_fragments
66function envoi_replace_fragments($replaces) {
67        $desc = $GLOBALS['tables_auxiliaires']['spip_versions_fragments'];
68        foreach($replaces as $r)
69                sql_replace('spip_versions_fragments', $r, $desc);
70}
71
72
73// http://doc.spip.org/@envoi_delete_fragments
74function envoi_delete_fragments($id_objet,$objet, $deletes) {
75        if (count($deletes)) {
76                sql_delete("spip_versions_fragments", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND ((".  join(") OR (", $deletes)."))");
77        }
78}
79
80
81//
82// Ajouter les fragments de la derniere version (tableau associatif id_fragment => texte)
83//
84// http://doc.spip.org/@ajouter_fragments
85function ajouter_fragments($id_objet,$objet, $id_version, $fragments) {
86        global $agregation_versions;
87
88        $replaces = array();
89        foreach ($fragments as $id_fragment => $texte) {
90                $nouveau = true;
91                // Recuperer la version la plus recente
92                $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");
93
94                if ($row) {
95                        $fragment = $row['fragment'];
96                        $version_min = $row['version_min'];
97                        if ($row['compress'] > 0) $fragment = @gzuncompress($fragment);
98                        $fragment = unserialize($fragment);
99                        if (is_array($fragment)) {
100                                unset($fragment[$id_version]);
101                                // Si le fragment n'est pas trop gros, prolonger celui-ci
102                                $nouveau = count($fragment) >= $agregation_versions
103                                        && strlen($row['fragment']) > 1000;
104                        }
105                }
106                if ($nouveau) {
107                        $fragment = array($id_version => $texte);
108                        $version_min = $id_version;
109                }
110                else {
111                        // Ne pas dupliquer les fragments non modifies
112                        $modif = true;
113                        for ($i = $id_version - 1; $i >= $version_min; $i--) {
114                                if (isset($fragment[$i])) {
115                                        $modif = ($fragment[$i] != $texte);
116                                        break;
117                                }
118                        }
119                        if ($modif) $fragment[$id_version] = $texte;
120                }
121
122                // Preparer l'enregistrement du fragment
123                $replaces[] = replace_fragment($id_objet,$objet, $version_min, $id_version, $id_fragment, $fragment);
124        }
125
126        envoi_replace_fragments($replaces);
127}
128
129//
130// Supprimer tous les fragments d'un objet lies a un intervalle de versions
131// (essaie d'eviter une trop grande fragmentation)
132//
133// http://doc.spip.org/@supprimer_fragments
134function supprimer_fragments($id_objet,$objet, $version_debut, $version_fin) {
135        global $agregation_versions;
136
137        $replaces = array();
138        $deletes = array();
139
140        // D'abord, vider les fragments inutiles
141        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");
142
143        // Fragments chevauchant l'ensemble de l'intervalle, s'ils existent
144        $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");
145
146        while ($row = sql_fetch($result)) {
147                $id_fragment = $row['id_fragment'];
148                $fragment = $row['fragment'];
149                if ($row['compress'] > 0) $fragment = gzuncompress($fragment);
150                $fragment = unserialize($fragment);
151                for ($i = $version_fin; $i >= $version_debut; $i--) {
152                        if (isset($fragment[$i])) {
153                                // Recopier le dernier fragment si implicite
154                                if (!isset($fragment[$version_fin + 1]))
155                                        $fragment[$version_fin + 1] = $fragment[$i];
156                                unset($fragment[$i]);
157                        }
158                }
159
160                $replaces[] = replace_fragment($id_objet,$objet,
161                        $row['version_min'], $row['version_max'], $id_fragment, $fragment);
162        }
163
164        // Fragments chevauchant le debut de l'intervalle, s'ils existent
165        $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");
166
167        $deb_fragment = array();
168        while ($row = sql_fetch($result)) {
169                $id_fragment = $row['id_fragment'];
170                $fragment = $row['fragment'];
171                $version_min = $row['version_min'];
172                $version_max = $row['version_max'];
173                if ($row['compress'] > 0) $fragment = gzuncompress($fragment);
174                $fragment = unserialize($fragment);
175                for ($i = $version_debut; $i <= $version_max; $i++) {
176                        if (isset($fragment[$i])) unset($fragment[$i]);
177                }
178
179                // Stocker temporairement le fragment pour agregation
180                $deb_fragment[$id_fragment] = $fragment;
181                // Ajuster l'intervalle des versions
182                $deb_version_min[$id_fragment] = $version_min;
183                $deb_version_max[$id_fragment] = $version_debut - 1;
184        }
185
186        // Fragments chevauchant la fin de l'intervalle, s'ils existent
187        $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");
188
189        while ($row = sql_fetch($result)) {
190                $id_fragment = $row['id_fragment'];
191                $fragment = $row['fragment'];
192                $version_min = $row['version_min'];
193                $version_max = $row['version_max'];
194                if ($row['compress'] > 0) $fragment = gzuncompress($fragment);
195                $fragment = unserialize($fragment);
196                for ($i = $version_fin; $i >= $version_min; $i--) {
197                        if (isset($fragment[$i])) {
198                                // Recopier le dernier fragment si implicite
199                                if (!isset($fragment[$version_fin + 1]))
200                                        $fragment[$version_fin + 1] = $fragment[$i];
201                                unset($fragment[$i]);
202                        }
203                }
204
205                // Virer l'ancien enregistrement (la cle primaire va changer)
206                $deletes[] = "id_fragment=$id_fragment AND version_min=$version_min";
207                // Essayer l'agregation
208                $agreger = false;
209                if (isset($deb_fragment[$id_fragment])) {
210                        $agreger = (count($deb_fragment[$id_fragment]) + count($fragment) <= $agregation_versions);
211                        if ($agreger) {
212                                $fragment = $deb_fragment[$id_fragment] + $fragment;
213                                $version_min = $deb_version_min[$id_fragment];
214                        }
215                        else {
216                                $replaces[] = replace_fragment($id_objet,$objet,
217                                        $deb_version_min[$id_fragment], $deb_version_max[$id_fragment],
218                                        $id_fragment, $deb_fragment[$id_fragment]);
219                        }
220                        unset($deb_fragment[$id_fragment]);
221                }
222                if (!$agreger) {
223                        // Ajuster l'intervalle des versions
224                        $version_min = $version_fin + 1;
225                }
226                $replaces[] = replace_fragment($id_objet, $objet, $version_min, $version_max, $id_fragment, $fragment);
227        }
228
229        // Ajouter fragments restants
230        if (is_array($deb_fragment) && count($deb_fragment) > 0) {
231                foreach ($deb_fragment as $id_fragment => $fragment) {
232                        $replaces[] = replace_fragment($id_objet,$objet,
233                                $deb_version_min[$id_fragment], $deb_version_max[$id_fragment],
234                                $id_fragment, $deb_fragment[$id_fragment]);
235                }
236        }
237
238        envoi_replace_fragments($replaces);
239        envoi_delete_fragments($id_objet,$objet, $deletes);
240}
241
242//
243// Recuperer les fragments d'une version donnee
244// renvoie un tableau associatif (id_fragment => texte)
245//
246// http://doc.spip.org/@recuperer_fragments
247function recuperer_fragments($id_objet,$objet, $id_version) {
248        $fragments = array();
249
250        if ($id_version == 0) return array();
251
252        $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");
253
254        while ($row = sql_fetch($result)) {
255                $id_fragment = $row['id_fragment'];
256                $version_min = $row['version_min'];
257                $fragment = $row['fragment'];
258                if ($row['compress'] > 0){
259                        $fragment_ = @gzuncompress($fragment);
260                        if (strlen($fragment) && $fragment_===false)
261                                $fragment=serialize(array($row['version_max']=>"["._T('forum_titre_erreur').$id_fragment."]"));
262                        else
263                         $fragment = $fragment_;
264                }
265                $fragment_ = unserialize($fragment);
266                if (strlen($fragment) && $fragment_===false)
267                        $fragment=array($row['version_max']=>"["._T('forum_titre_erreur').$id_fragment."]");
268                else
269                 $fragment = $fragment_;
270                for ($i = $id_version; $i >= $version_min; $i--) {
271                        if (isset($fragment[$i])) {
272
273                                ## hack destine a sauver les archives des sites iso-8859-1
274                                ## convertis en utf-8 (les archives ne sont pas converties
275                                ## mais ce code va les nettoyer ; pour les autres charsets
276                                ## la situation n'est pas meilleure ni pire qu'avant)
277                                if ($GLOBALS['meta']['charset'] == 'utf-8'
278                                AND include_spip('inc/charsets')
279                                AND !is_utf8($fragment[$i])) {
280                                        $fragment[$i] = importer_charset($fragment[$i], 'iso-8859-1');
281                                }
282
283                                $fragments[$id_fragment] = $fragment[$i];
284                                break;
285                        }
286                }
287        }
288        return $fragments;
289}
290
291
292//
293// Apparier des paragraphes deux a deux entre une version originale
294// et une version modifiee
295//
296// http://doc.spip.org/@apparier_paras
297function apparier_paras($src, $dest, $flou = true) {
298        $src_dest = array();
299        $dest_src = array();
300
301        $t1 = $t2 = array();
302
303        $md1 = $md2 = array();
304        $gz_min1 = $gz_min2 = array();
305        $gz_trans1 = $gz_trans2 = array();
306        $l1 = $l2 = array();
307
308        // Nettoyage de la ponctuation pour faciliter l'appariement
309        foreach($src as $key => $val) {
310                $t1[$key] = strval(preg_replace("/[[:punct:][:space:]]+/", " ", $val));
311        }
312        foreach($dest as $key => $val) {
313                $t2[$key] = strval(preg_replace("/[[:punct:][:space:]]+/", " ", $val));
314        }
315
316        // Premiere passe : chercher les correspondance exactes
317        foreach($t1 as $key => $val) $md1[$key] = md5($val);
318        foreach($t2 as $key => $val) $md2[md5($val)][$key] = $key;
319        foreach($md1 as $key1 => $h) {
320                if (isset($md2[$h])) {
321                        $key2 = reset($md2[$h]);
322                        if (isset($t1[$key1]) AND isset($t2[$key2]) AND $t1[$key1] == $t2[$key2]) {
323                                $src_dest[$key1] = $key2;
324                                $dest_src[$key2] = $key1;
325                                unset($t1[$key1]);
326                                unset($t2[$key2]);
327                                unset($md2[$h][$key2]);
328                        }
329                }
330        }
331
332        if ($flou) {
333                // Deuxieme passe : recherche de correlation par test de compressibilite
334                foreach($t1 as $key => $val) {
335                        $l1[$key] = strlen(gzcompress($val));
336                }
337                foreach($t2 as $key => $val) {
338                        $l2[$key] = strlen(gzcompress($val));
339                }
340                foreach($t1 as $key1 => $s1) {
341                        foreach($t2 as $key2 => $s2) {
342                                $r = strlen(gzcompress($s1.$s2));
343                                $taux = 1.0 * $r / ($l1[$key1] + $l2[$key2]);
344                                if (!isset($gz_min1[$key1]) || !$gz_min1[$key1] || $gz_min1[$key1] > $taux) {
345                                        $gz_min1[$key1] = $taux;
346                                        $gz_trans1[$key1] = $key2;
347                                }
348                                if (!isset($gz_min2[$key2]) ||!$gz_min2[$key2] || $gz_min2[$key2] > $taux) {
349                                        $gz_min2[$key2] = $taux;
350                                        $gz_trans2[$key2] = $key1;
351                                }
352                        }
353                }
354
355                // Depouiller les resultats de la deuxieme passe :
356                // ne retenir que les correlations reciproques
357                foreach($gz_trans1 as $key1 => $key2) {
358                        if ($gz_trans2[$key2] == $key1 && $gz_min1[$key1] < 0.9) {
359                                $src_dest[$key1] = $key2;
360                                $dest_src[$key2] = $key1;
361                        }
362                }
363        }
364
365        // Retourner les mappings
366        return array($src_dest, $dest_src);
367}
368
369//
370// Recuperer les champs d'une version donnee
371//
372// http://doc.spip.org/@recuperer_version
373function recuperer_version($id_objet,$objet, $id_version) {
374
375        $champs = sql_getfetsel("champs", "spip_versions", "id_objet=" . intval($id_objet) . " AND objet=".sql_quote($objet)." AND id_version=" . intval($id_version));
376        if (!$champs OR !is_array($champs = unserialize($champs)))
377                return array();
378        else return reconstuire_version($champs,
379                         recuperer_fragments($id_objet,$objet, $id_version));
380}
381
382// http://doc.spip.org/@reconstuire_version
383function reconstuire_version($champs, $fragments, $res=array()) {
384
385        static $msg;
386        if (!$msg) $msg = _T('forum_titre_erreur');
387
388        foreach ($champs as $nom => $code) {
389                if (!isset($res[$nom])) {
390                        $t = '';
391                        foreach (array_filter(explode(' ', $code)) as $id) {
392                                $t .= isset($fragments[$id])
393                                        ? $fragments[$id]
394                                        : "[$msg$id]";
395                        }
396                        $res[$nom] = $t;
397                }
398        }
399        return $res;
400}
401
402// http://doc.spip.org/@supprimer_versions
403function supprimer_versions($id_objet,$objet, $version_min, $version_max) {
404        sql_delete("spip_versions", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version>=$version_min AND id_version<=$version_max");
405
406        supprimer_fragments($id_objet,$objet, $version_min, $version_max);
407}
408
409//
410// Ajouter une version a un objet
411//
412// http://doc.spip.org/@ajouter_version
413function ajouter_version($id_objet,$objet, $champs, $titre_version = "", $id_auteur) {
414        $paras = $paras_old = $paras_champ = $fragments = array();
415
416        // Attention a une edition anonyme (type wiki): id_auteur n'est pas
417        // definie, on enregistre alors le numero IP
418        $str_auteur = intval($id_auteur) ? intval($id_auteur) : $GLOBALS['ip'];
419        // si pas de titre dans cette version, la marquer 'non' permanente,
420        // et elle pourra etre fusionnee avec une revision ulterieure dans un delai < _INTERVALLE_REVISIONS
421        // permet de fusionner plusieurs editions consecutives champs par champs avec les crayons
422        $permanent = empty($titre_version) ? 'non' : '';
423
424        // Detruire les tentatives d'archivages non abouties en 1 heure
425        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)");
426
427        // Signaler qu'on opere en mettant un numero de version negatif
428        // distinctif (pour eviter la violation d'unicite)
429        // et un titre contenant en fait le moment de l'insertion
430        list($ms, $sec) = explode(' ', microtime());
431        $date = $sec . substr($ms,1,4); // SQL ne ramene que 4 chiffres significatifs apres la virgule pour 0.0+titre_version
432        $datediff = ($sec - mktime(0,0,0,9,1,2007)) * 1000000 + substr($ms,2, strlen($ms)-4);
433
434        $valeurs = array('id_objet' => $id_objet,
435                         'objet' => $objet,
436                         'id_version' => (0 - $datediff),
437                         'date' => date('Y-m-d H:i:s'),
438                         'id_auteur' => $str_auteur, //  varchar ici!
439                         'titre_version' => $date);
440        sql_insertq('spip_versions',  $valeurs);
441
442        // Eviter les validations entremelees en s'endormant s'il existe
443        // une version <0 plus recente mais pas plus vieille que 10s
444        // Une <0 encore plus vieille est une operation avortee,
445        // on passe outre (vaut mieux archiver mal que pas du tout).
446        // Pour tester:
447        // 0. mettre le delai a 30
448        // 1. decommenter le premier sleep(15)
449        // 2. enregistrer une modif
450        // 3. recommenter le premier sleep(15), decommenter le second.
451        // 4. enregistrer une autre modif dans les 15 secondes
452#         sleep(15);
453        $delai = $sec-10;
454        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 0.0+titre_version > $delai")) {
455                spip_log("version $objet $id_objet :insertion en cours avant $date ($delai)");
456                sleep(1);
457                $delai++;
458        }
459#   sleep(15);  spip_log("sortie $sec $delai");
460        // Determiner le numero du prochain fragment
461        $next = sql_fetsel("id_fragment", "spip_versions_fragments", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet), "", "id_fragment DESC", "1");
462
463        $onlylock = '';
464
465        // Examiner la derniere version
466        $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
467
468        if ($row) {
469                $id_version = $row['id_version'];
470                $paras_old = recuperer_fragments($id_objet,$objet, $id_version);
471                $champs_old = $row['champs'];
472                if ($row['id_auteur']!= $str_auteur
473                  OR $row['permanent']!='non'
474                  OR strtotime($row['date']) < (time()-_INTERVALLE_REVISIONS)) {
475                        spip_log(strtotime($row['date']), 'revisions');
476                        spip_log(time(), 'revisions');
477                        spip_log(_INTERVALLE_REVISIONS, 'revisions');
478                        $id_version++;
479                }
480                // version precedente recente, on va la mettre a jour
481                // avec les nouveaux arrivants si presents
482                else {
483                        $champs = reconstuire_version(unserialize($champs_old), $paras_old, $champs);
484                        $onlylock = 're';
485                }
486        } else
487                $id_version = 1;
488
489        spip_log($str_auteur, 'revisions');
490        spip_log($row, 'revisions');
491        spip_log($id_version, 'revisions');
492
493        $next = !$next ? 1 : ($next['id_fragment'] + 1);
494
495        // Generer les nouveaux fragments
496        $codes = array();
497        foreach ($champs as $nom => $texte) {
498                $codes[$nom] = array();
499                $paras = separer_paras($texte, $paras);
500                $paras_champ[$nom] = count($paras);
501        }
502
503        // Apparier les fragments de maniere optimale
504        $n = count($paras);
505        if ($n) {
506                // Tables d'appariement dans les deux sens
507                list(,$trans) = apparier_paras($paras_old, $paras);
508                reset($champs);
509                $nom = '';
510
511                // eviter une notice PHP au tout debut de la boucle
512                // on ajoute ''=>0 en debut de tableau.
513                $paras_champ = array($nom=>0) + $paras_champ;
514
515                for ($i = 0; $i < $n; $i++) {
516                        while ($i >= $paras_champ[$nom]) list($nom, ) = each($champs);
517                        // Lier au fragment existant si possible, sinon creer un nouveau fragment
518                        $id_fragment = isset($trans[$i]) ? $trans[$i] : $next++;
519                        $codes[$nom][] = $id_fragment;
520                        $fragments[$id_fragment] = $paras[$i];
521                }
522        }
523        foreach ($champs as $nom => $t) {
524                $codes[$nom] = join(' ', $codes[$nom]);
525                # avec la ligne qui suit, un champ qu'on vide ne s'enregistre pas
526                # if (!strlen($codes[$nom])) unset($codes[$nom]);
527        }
528
529        // Enregistrer les modifications
530        ajouter_fragments($id_objet,$objet, $id_version, $fragments);
531
532        // Si l'insertion ne servait que de verrou,
533        // la detruire apres mise a jour de l'ancienne entree,
534        // sinon la mise a jour efface en fait le verrou.
535
536        if (!$onlylock) {
537                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'");
538        } else {
539                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");
540
541                sql_delete("spip_versions", "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version < 0 AND titre_version ='$date'");
542        }
543        spip_log($onlylock . "memorise la version $id_version de l'objet $objet $id_objet $titre_version");
544
545        return $id_version;
546}
547
548// les textes "diff" ne peuvent pas passer dans propre directement,
549// car ils contiennent des <span> et <div> parfois mal places
550// http://doc.spip.org/@propre_diff
551function propre_diff($texte) {
552
553        $span_diff = array();
554        if (preg_match_all(',<(/)?(span|div) (class|rem)="diff-[^>]*>,', $texte, $regs, PREG_SET_ORDER)) {
555                $regs = array_slice($regs,0,500); #limiter la casse s'il y en a trop
556                foreach ($regs as $c => $reg) {
557                        $texte = str_replace($reg[0], '@@@SPIP_DIFF'.$c.'@@@', $texte);
558                }
559        }
560
561        // [ ...<span diff> -> lien ]
562        // < tag <span diff> >
563        $texte = preg_replace(',<([^>]*?@@@SPIP_DIFF[0-9]+@@@),',
564                '&lt;\1', $texte);
565
566        # attention ici astuce seulement deux @@ finals car on doit eviter
567        # deux patterns a suivre, afin de pouvoir prendre [ mais eviter [[
568        $texte = preg_replace(',(^|[^[])[[]([^[\]]*@@@SPIP_DIFF[0-9]+@@),',
569                '\1&#91;\2', $texte);
570
571        // desactiver TeX & toujours-paragrapher
572        $tex = $GLOBALS['traiter_math'];
573        $GLOBALS['traiter_math'] = '';
574        $mem = $GLOBALS['toujours_paragrapher'];
575        $GLOBALS['toujours_paragrapher'] = false;
576
577        $texte = propre($texte);
578
579        // retablir
580        $GLOBALS['traiter_math'] = $tex;
581        $GLOBALS['toujours_paragrapher'] = $mem;
582
583        // un blockquote mal ferme peut gener l'affichage, et title plante safari
584        $texte = preg_replace(',<(/?(blockquote|title)[^>]*)>,i', '&lt;\1>', $texte);
585
586        // Dans les <cadre> c'est un peu plus complique
587        if (preg_match_all(',<textarea (.*)</textarea>,Uims', $texte, $area, PREG_SET_ORDER)) {
588                foreach ($area as $reg) {
589                        $remplace = preg_replace(',@@@SPIP_DIFF[0-9]+@@@,', '**', $reg[0]);
590                        if ($remplace <> $reg[0])
591                                $texte = str_replace($reg[0], $remplace, $texte);
592                }
593        }
594
595        // replacer les valeurs des <span> et <div> diff-
596        if (is_array($regs))
597        foreach ($regs as $c => $reg) {
598                $bal = (!$reg[1]) ? $reg[0] : "</$reg[2]>";
599                $texte = str_replace('@@@SPIP_DIFF'.$c.'@@@', $bal, $texte);
600                $GLOBALS['les_notes'] = str_replace('@@@SPIP_DIFF'.$c.'@@@', $bal, $GLOBALS['les_notes']);
601        }
602
603
604        // quand le dernier tag est ouvrant le refermer ...
605        $reg = end($regs);
606        if (!$reg[1] AND $reg[2]) $texte.="</$reg[2]>";
607
608        return $texte;
609}
610
611
612/**
613 * Liste les champs versionnés d'une table objet.
614 *
615 * @param string $table
616 *     Nom complet de sa table sql. Exemple 'spip_articles'
617 * @return array
618 *     Liste des champs versionnés
619 */
620function liste_champs_versionnes($table) {
621        $liste_objets_versionnees = is_array(unserialize($GLOBALS['meta']['objets_versions'])) ? unserialize($GLOBALS['meta']['objets_versions']) : array();
622
623        if (!in_array($table,$liste_objets_versionnees))
624                return array();
625
626        include_spip('base/objets');
627        if ($infos=lister_tables_objets_sql($table)
628          AND isset($infos['champs_versionnes']))
629                return $infos['champs_versionnes'];
630
631        return array();
632}
633
634/**
635 * Lorsqu'un champ versionée est une jointure, récuperer tous les liens
636 * et les mettre sous forme de liste énumérée
637 *
638 * @param string $objet
639 * @param string $id_objet
640 * @param string $jointure
641 * @return string
642 */
643function recuperer_valeur_champ_jointure($objet,$id_objet,$jointure){
644        $objet_joint = objet_type($jointure);
645        include_spip('action/editer_liens');
646        $v = array();
647        if (objet_associable($objet_joint)) {
648                $liens = objet_trouver_liens(array($objet_joint=>'*'),array($objet=>$id_objet));
649                foreach($liens as $l)
650                        $v[] = $l[$objet_joint];
651        }
652        elseif(objet_associable($objet)) {
653                $liens = objet_trouver_liens(array($objet=>$id_objet),array($objet_joint=>'*'));
654                foreach($liens as $l)
655                        $v[] = $l[$objet];
656        }
657        sort($v);
658        return implode(",",$v);
659}
660
661/**
662 * Créer la première révision d'un objet si nécessaire
663 *
664 * À faire notamment si on vient d'activer l'extension et qu'on fait une modif
665 * sur un objet qui était déjà en base, mais non versionné
666 *
667 * La fonction renvoie le numéro de la dernière version de l'objet,
668 * et 0 si pas de version pour cet objet
669 *
670 * @param string $table
671 * @param string $objet
672 * @param int $id_objet
673 * @param array $champs
674 * @param int $id_auteur
675 * @return int
676 */
677function verifier_premiere_revision($table,$objet,$id_objet,$champs=null, $id_auteur=0){
678
679        $id_table_objet = id_table_objet($objet);
680        if (!$champs)
681                $champs = liste_champs_versionnes($table);
682        if (!$champs)
683                return false;
684
685        if (!$id_version = sql_getfetsel('id_version','spip_versions',"id_objet=".intval($id_objet)." AND objet=".sql_quote($objet),'','id_version DESC','0,1')) {
686                // recuperer toutes les valeurs actuelles des champs
687                // pour l'objet
688                $originaux = sql_fetsel("*", $table, "$id_table_objet=".intval($id_objet));
689                $premiere = false;
690                foreach($champs as $v){
691                        if (isset($originaux[$v])){
692                                $champs_originaux[$v] = $originaux[$v];
693                        }
694                        else if(strncmp($v,'jointure_',9)==0) {
695                                $champs_originaux[$v] = recuperer_valeur_champ_jointure($objet,$id_objet,substr($v,9));
696                        }
697                        if (isset($champs_originaux[$v]) AND strlen($originaux[$v]))
698                                $premiere = true;
699                }
700
701                // Si un champ est non vide,
702                // il faut creer une premiere revision
703                if ($premiere) {
704                        $trouver_table = charger_fonction('trouver_table','base');
705                        $desc = $trouver_table($table);
706
707                        // "trouver" une date raisonnable pour la version initiale
708
709                        $date_modif = "";
710                        foreach(array('date_modif','maj') as $d){
711                                if (!$date_modif AND isset($originaux[$d]) AND $t=strtotime($d))
712                                        $date_modif = date("Y-m-d H:i:s", $t);
713                        }
714                        if (!$date_modif
715                          AND isset($desc['date'])
716                          AND isset($originaux[$desc['date']])) {
717                                $date_modif = $originaux[$desc['date']];
718                        }
719                        elseif (!$date_modif)
720                                $date_modif = date("Y-m-d H:i:s", time()-7200);
721                       
722                        if ($id_version = ajouter_version($id_objet, $objet, $champs_originaux, _T('revisions:version_initiale'), $id_auteur))
723                                sql_updateq('spip_versions', array('date' => $date_modif), "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version=$id_version");
724                }
725        }
726        return $id_version;
727}
728
729?>
Note: See TracBrowser for help on using the repository browser.