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

Last change on this file since 100912 was 100912, checked in by maieul@…, 3 years ago

report de r100911: première révision d'un objet: prendre effectivement en priorité le champ date_modif ou maj comme date de révision

File size: 25.8 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2016                                                *
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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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 titre_version<>".sql_quote($date,'','text')." 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://code.spip.net/@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        // et interdire_scripts !
609        $texte = interdire_scripts($texte);
610
611        return $texte;
612}
613
614
615/**
616 * Liste les champs versionnés d'une table objet.
617 *
618 * @param string $table
619 *     Nom complet de sa table sql. Exemple 'spip_articles'
620 * @return array
621 *     Liste des champs versionnés
622 */
623function liste_champs_versionnes($table) {
624        $liste_objets_versionnees = is_array(unserialize($GLOBALS['meta']['objets_versions'])) ? unserialize($GLOBALS['meta']['objets_versions']) : array();
625
626        if (!in_array($table,$liste_objets_versionnees))
627                return array();
628
629        include_spip('base/objets');
630        if ($infos=lister_tables_objets_sql($table)
631          AND isset($infos['champs_versionnes']))
632                return $infos['champs_versionnes'];
633
634        return array();
635}
636
637/**
638 * Lorsqu'un champ versionée est une jointure, récuperer tous les liens
639 * et les mettre sous forme de liste énumérée
640 *
641 * @param string $objet
642 * @param string $id_objet
643 * @param string $jointure
644 * @return string
645 */
646function recuperer_valeur_champ_jointure($objet,$id_objet,$jointure){
647        $objet_joint = objet_type($jointure);
648        include_spip('action/editer_liens');
649        $v = array();
650        if (objet_associable($objet_joint)) {
651                $liens = objet_trouver_liens(array($objet_joint=>'*'),array($objet=>$id_objet));
652                foreach($liens as $l)
653                        $v[] = $l[$objet_joint];
654        }
655        elseif(objet_associable($objet)) {
656                $liens = objet_trouver_liens(array($objet=>$id_objet),array($objet_joint=>'*'));
657                foreach($liens as $l)
658                        $v[] = $l[$objet];
659        }
660        sort($v);
661        return implode(",",$v);
662}
663
664/**
665 * Créer la première révision d'un objet si nécessaire
666 *
667 * À faire notamment si on vient d'activer l'extension et qu'on fait une modif
668 * sur un objet qui était déjà en base, mais non versionné
669 *
670 * La fonction renvoie le numéro de la dernière version de l'objet,
671 * et 0 si pas de version pour cet objet
672 *
673 * @param string $table
674 * @param string $objet
675 * @param int $id_objet
676 * @param array $champs
677 * @param int $id_auteur
678 * @return int
679 */
680function verifier_premiere_revision($table,$objet,$id_objet,$champs=null, $id_auteur=0){
681
682        $id_table_objet = id_table_objet($objet);
683        if (!$champs)
684                $champs = liste_champs_versionnes($table);
685        if (!$champs)
686                return false;
687
688        if (!$id_version = sql_getfetsel('id_version','spip_versions',"id_objet=".intval($id_objet)." AND objet=".sql_quote($objet),'','id_version DESC','0,1')) {
689                // recuperer toutes les valeurs actuelles des champs
690                // pour l'objet
691                $originaux = sql_fetsel("*", $table, "$id_table_objet=".intval($id_objet));
692                $premiere = false;
693                foreach($champs as $v){
694                        if (isset($originaux[$v])){
695                                $champs_originaux[$v] = $originaux[$v];
696                        }
697                        else if(strncmp($v,'jointure_',9)==0) {
698                                $champs_originaux[$v] = recuperer_valeur_champ_jointure($objet,$id_objet,substr($v,9));
699                        }
700                        if (isset($champs_originaux[$v]) AND strlen($originaux[$v]))
701                                $premiere = true;
702                }
703
704                // Si un champ est non vide,
705                // il faut creer une premiere revision
706                if ($premiere) {
707                        $trouver_table = charger_fonction('trouver_table','base');
708                        $desc = $trouver_table($table);
709
710                        // "trouver" une date raisonnable pour la version initiale
711
712                        $date_modif = "";
713                        foreach(array('date_modif','maj') as $d){
714                                if (!$date_modif AND isset($originaux[$d]) AND $t=strtotime($originaux[$d]))
715                                        $date_modif = date("Y-m-d H:i:s", $t);
716                        }
717                        if (!$date_modif
718                          AND isset($desc['date'])
719                          AND isset($originaux[$desc['date']])) {
720                                $date_modif = $originaux[$desc['date']];
721                        }
722                        elseif (!$date_modif)
723                                $date_modif = date("Y-m-d H:i:s", time()-7200);
724                       
725                        if ($id_version = ajouter_version($id_objet, $objet, $champs_originaux, _T('revisions:version_initiale'), $id_auteur))
726                                sql_updateq('spip_versions', array('date' => $date_modif), "id_objet=".intval($id_objet)." AND objet=".sql_quote($objet)." AND id_version=$id_version");
727                }
728        }
729        return $id_version;
730}
731
732?>
Note: See TracBrowser for help on using the repository browser.