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

Last change on this file since 93092 was 93092, checked in by gilles.vincent@…, 4 years ago

Mise en forme plus homegene et plus lisible, pour les declarations des fonctions
Regles :

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