source: spip-zone/_core_/plugins/revisions/inc/revisions.php @ 45623

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

support des revisions sur les jointure : il suffit de definir 'jointure_{table}' comme champ a versionner (avec table = version abregee 'mots', 'auteurs'...)
Il faut que les liens soient gérés par l'api editer_lien pour que les pipelines pre_edition_lien et post_edition_lien soient appelés er declenchent la gestion de revision.
Pour l'affichage des diff, une fonction afficher_diff_jointure generique est utilisee pour les jointures, mais il est possible de definir sa propre fonction d'affichage afficher_diff_jointure_xxx comme pour les champs standards.

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