[SPIP] ~2.1.12 -->2.1.25
[velocampus/web/www.git] / www / ecrire / inc / export.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2014 *
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 if (!defined('_ECRIRE_INC_VERSION')) return;
14
15 define('_EXPORT_TRANCHES_LIMITE', 200);
16 define('_EXTENSION_PARTIES', '.gz');
17
18 function inc_export_dist($meta)
19 {
20 if (!isset($GLOBALS['meta'][$meta])) {
21 include_spip('inc/minipres');
22 echo minipres();
23 } else {
24 while (true) {
25 $val = unserialize($GLOBALS['meta'][$meta]);
26 if ($dir = export_repertoire($meta, $val))
27 export_trace($val, $dir, $meta);
28 else break;
29 }
30 list($gz, $archive, $rub,,,,$serveur,$save) = $val;
31 // retour a exec_export_all qui renverra sur action_export_all
32 return "end,$gz,$archive,$rub,$serveur,$save";
33 }
34 }
35
36 // calcule le repertoire de la sauvegarde
37 // et le nettoie au premier appel.
38 // Aux suivants, retourne le nom du repertoire ou rien si c'est fini.
39
40 function export_repertoire($meta, $val_meta)
41 {
42 list(, $archive, , $tables, $etape, $sous_etape, , $save) = $val_meta;
43 if (!function_exists('inc_export_' . ($save ? $save : 'xml'))) {
44 spip_log("fonction inc_export_$save indisponible");
45 return false;
46 }
47 $dir = base_dump_dir($meta);
48
49 // Reperer une situation anormale (echec reprise sur interruption)
50 if (($etape == 1) AND !$sous_etape) {
51 $file = $dir . $archive;
52 $l = preg_files($file . ".part_[0-9]+_[0-9]+");
53 if ($l) {
54 spip_log("menage d'une sauvegarde inachevee: " . join(',', $l));
55 foreach($l as $dummy) spip_unlink($dummy);
56 }
57 }
58 $all = count($tables);
59 return ($etape > $all OR !$all) ? false : $dir;
60 }
61
62 function export_trace($val_meta, $dir, $meta)
63 {
64 list($gz, $archive, $rub, $tables_for_dump, $etape_actuelle, $sous_etape, $serveur, $save) = $val_meta;
65 include_spip('inc/minipres');
66 // pour permettre l'affichage au fur et a mesure
67 @ini_set("zlib.output_compression","0");
68
69 if (!($timeout = ini_get('max_execution_time')*1000));
70 $timeout = 30000; // parions sur une valeur tellement courante ...
71 // le premier hit est moitie moins long car seulement une phase d'ecriture de morceaux
72 // sans ramassage
73 // sinon grosse ecriture au 1er hit, puis gros rammassage au deuxieme avec petite ecriture,... ca oscille
74 if (!$etape_actuelle AND !$sous_etape) {
75 $timeout = round($timeout/2);
76 $tables_sauvegardees = array();
77 } else {
78 $metatable = $meta . '_tables';
79 $tables_sauvegardees = isset($GLOBALS['meta'][$metatable])?unserialize($GLOBALS['meta'][$metatable]):array();
80 }
81
82 // Les sauvegardes partielles prennent le temps d'indiquer les logos
83 // Instancier une fois pour toutes, car on va boucler un max.
84 // On complete jusqu'au secteur pour resituer dans l'arborescence)
85 if ($rub) {
86 $GLOBALS['chercher_logo'] = charger_fonction('chercher_logo', 'inc',true);
87 $les_rubriques = complete_fils(array($rub), $serveur);
88 $les_meres = complete_secteurs(array($rub), $serveur);
89 } else {
90 $GLOBALS['chercher_logo'] = false;
91 $les_rubriques = $les_meres = '';
92 }
93
94 // script de rechargement auto sur timeout
95 $redirect = generer_url_ecrire("export_all");
96 $all = count($tables_for_dump);
97 echo ( install_debut_html(_T('info_sauvegarde') . " ($all)"));
98 echo http_script("window.setTimeout('location.href=\"".$redirect."\";',$timeout)");
99
100 echo "<div style='text-align: left'>\n";
101 $etape = 1;
102 foreach($tables_for_dump as $table){
103 if ($etape_actuelle > $etape) {
104 // sauter les deja faits, mais rappeler qu'ils sont fait
105 echo ( "\n<br /><strong>".$etape. '. '."</strong>". $tables_sauvegardees[$table]);
106 }
107 else {
108 echo ( "\n<br /><strong>".$etape. '. '. $table."</strong> ");
109 $r = sql_countsel($table, array(), array(), array(), $serveur);
110 flush();
111 if (!$r) $r = ( _T('texte_vide'));
112 else {
113 $f = $dir . $archive . '.part_' . sprintf('%03d',$etape);
114 $r = export_objets($table, $sous_etape, $r, $f, $les_rubriques, $les_meres, $meta);
115 $r += $sous_etape*_EXPORT_TRANCHES_LIMITE;
116 // info pas fiable si interruption+partiel
117 if ($rub AND $etape_actuelle > 1) $r = ">= $r";
118 }
119 echo " $r";
120 flush();
121 $sous_etape = 0;
122 // on utilise l'index comme ca c'est pas grave si on ecrit plusieurs fois la meme
123 $tables_sauvegardees[$table] = "$table ($r)";
124 ecrire_meta($meta . '_tables', serialize($tables_sauvegardees),'non');
125 }
126 $etape++;
127 $v = serialize(array($gz, $archive, $rub, $tables_for_dump, $etape,$sous_etape, $serveur, $save));
128 ecrire_meta($meta, $v,'non');
129 }
130 echo ( "</div>\n");
131 // si Javascript est dispo, anticiper le Time-out
132 echo ("<script language=\"JavaScript\" type=\"text/javascript\">window.setTimeout('location.href=\"$redirect\";',0);</script>\n");
133 echo (install_fin_html());
134 flush();
135 }
136
137 // http://doc.spip.org/@complete_secteurs
138 function complete_secteurs($les_rubriques, $serveur='')
139 {
140 $res = array();
141 foreach($les_rubriques as $r) {
142 do {
143 $r = sql_getfetsel("id_parent", "spip_rubriques", "id_rubrique=$r", array(), array(), '', array(), $serveur);
144 if ($r) {
145 if ((isset($les_rubriques[$r])) OR isset($res[$r]))
146 $r = false;
147 else $res[$r] = $r;
148 }
149 } while ($r);
150 }
151 return $res;
152 }
153
154 // http://doc.spip.org/@complete_fils
155 function complete_fils($rubriques, $serveur='')
156 {
157 $r = $rubriques;
158 do {
159 $q = sql_select("id_rubrique", "spip_rubriques", "id_parent IN (".join(',',$r).")", array(), array(), '', array(), $serveur);
160 $r = array();
161 while ($row = sql_fetch($q, $serveur)) {
162 $r[]= $rubriques[] = $row['id_rubrique'];
163 }
164 } while ($r);
165
166
167 return $rubriques;
168 }
169
170 //
171 // Exportation de table SQL au format xml
172 // La constante ci-dessus determine la taille des tranches,
173 // chaque tranche etant copiee immediatement dans un fichier
174 // et son numero memorisee dans le serveur SQL.
175 // En cas d'abandon sur Time-out, le travail pourra ainsi avancer.
176 // Au final, on regroupera les tranches en un seul fichier
177 // et on memorise dans le serveur qu'on va passer a la table suivante.
178 // on prefere ne pas faire le ramassage ici de peur d'etre interrompu
179 // par le timeout au mauvais moment
180 // le ramassage aura lieu en debut de hit suivant,
181 // et ne sera normalement pas interrompu car le temps pour ramasser
182 // est plus court que le temps pour creer les parties
183
184 // http://doc.spip.org/@export_objets
185 function export_objets($table, $cpt, $total, $filetable, $les_rubriques, $les_meres, $meta) {
186 global $tables_principales;
187
188 $temp = $filetable . '.temp' . _EXTENSION_PARTIES;
189 $prim = isset($tables_principales[$table])
190 ? $tables_principales[$table]['key']["PRIMARY KEY"]
191 : '';
192 $debut = $cpt * _EXPORT_TRANCHES_LIMITE;
193 $effectifs = 0;
194
195 $v = unserialize($GLOBALS['meta'][$meta]);
196 while (1){ // on ne connait pas le nb de paquets d'avance
197
198 $cpt++;
199 $tranche = build_while($debut, $table, $prim, $les_rubriques, $les_meres, $v[7], $v[6]);
200 // attention: vide ne suffit pas a sortir
201 // car les sauvegardes partielles peuvent parcourir
202 // une table dont la portion qui les concerne sera vide..
203 if ($tranche) {
204 // on ecrit dans un fichier generique
205 // puis on le renomme pour avoir une operation atomique
206 if (is_array($tranche)) {
207 ecrire_fichier ($temp, join('', $tranche));
208 $f = $filetable . sprintf('_%04d',$cpt) . _EXTENSION_PARTIES;
209 // le fichier destination peut deja exister
210 // si on sort d'un timeout entre le rename et le ecrire_meta
211 if (file_exists($f)) spip_unlink($f);
212 rename($temp, $f);
213 $tranche = count($tranche);
214 }
215 $effectifs += $tranche;
216 }
217 // incrementer le numero de sous-etape
218 // au cas ou une interruption interviendrait
219 $v[5]++;
220 ecrire_meta($meta, serialize($v));
221 $debut += _EXPORT_TRANCHES_LIMITE;
222 if ($debut >= $total) {break;}
223 /* pour tester la robustesse de la reprise sur interruption
224 decommenter ce qui suit.
225 if ($cpt && 1) {
226 spip_log("force interrup $s");
227 include_spip('inc/headers');
228 redirige_par_entete("./?exec=export_all&rub=$rub&x=$s");
229 } /* */
230 echo(". ");
231 flush();
232 }
233 return $effectifs;
234 }
235
236 // sauvegarde d'une table par ordre croissant de la cle primaire simple
237 // sinon les sequences PG seront pertubees a la restauration
238 // (a ameliorer)
239 // Retourne un tableau de chaines, d'autant d'element que de Row
240 // ou leur nombre, selon la fonction utilisee
241 // http://doc.spip.org/@build_while
242 function build_while($debut, $table, $prim, $les_rubriques, $les_meres, $save='', $serveur='') {
243
244 $result = sql_select('*', $table, '', '', $prim, "$debut," . _EXPORT_TRANCHES_LIMITE, array(), $serveur);
245
246 $i = 0;
247 $res = array();
248 $save = 'inc_export_' . ($save ? $save : 'xml');
249 while ($r = sql_fetch($result, $serveur)) {
250 if (export_select($r, $les_rubriques, $les_meres)) {
251 if ($s = $save($r, $table, $prim, $serveur)) $res[]=$s; else $i++;
252 }
253 }
254 sql_free($result, $serveur);
255 return $res ? $res : $i;
256 }
257
258 // Construit la version xml des champs d'une table
259 function inc_export_xml($row, $table, $prim, $serveur) {
260 global $chercher_logo ;
261 if ($chercher_logo) {
262 $on = $chercher_logo($row[$prim], $prim, 'on');
263 $off = $chercher_logo($row[$prim], $prim, 'off');
264 } else $on = $off = "";
265 foreach ($row as $k => $v) {
266 $row[$k] = "<$k>" . text_to_xml($v) . "</$k>";
267 }
268 return "<$table"
269 . ($on ? " on='$on[3]'" : '')
270 . ($off ? " off='$off[3]'" : '')
271 . ">\n"
272 . join("\n", $row)
273 . "\n</$table>\n\n";
274 }
275
276 // dit si Row est exportable,
277 // en particulier quand on se restreint a certaines rubriques
278 // La table des documents doit etre apres celle des liens de doc
279 // elle-meme apres celle des objets auxquels elle lie ces documents.
280
281 // http://doc.spip.org/@export_select
282 function export_select($row, $les_rubriques, $les_meres) {
283 static $articles = array();
284 static $documents = array();
285
286 if (isset($row['impt']) AND $row['impt'] !='oui') return false;
287 if (!$les_rubriques) return true;
288
289 // numero de rubrique non determinant pour les forums (0 a 99%)
290 if (isset($row['id_rubrique']) AND $row['id_rubrique']) {
291 if (in_array($row['id_rubrique'], $les_rubriques)) {
292 if (isset($row['id_article']))
293 $articles[] = $row['id_article'];
294 if (isset($row['id_document']))
295 $documents[]=$row['id_document'];
296 return true;
297 }
298 if (!in_array($row['id_rubrique'], $les_meres))
299 return false;
300 // la rubrique, mais rien d'autre
301 return (!isset($row['id_article'])
302 AND !isset($row['id_mot'])
303 AND !isset($row['id_document'])
304 AND !isset($row['id_breve']));
305 }
306 // dependances d'articles (mots, petitions, signatures et documents)
307 if (isset($row['id_article']) AND $row['id_article']) {
308 if (in_array($row['id_article'], $articles)) {
309 if (isset($row['id_document']))
310 $documents[]= $row['id_document'];
311 return true;
312 }
313 return false;
314 }
315 if (isset($row['id_objet']) AND isset($row['objet'])) {
316 if ($row['objet'] == 'article') {
317 if (in_array($row['id_objet'], $articles)) {
318 if (isset($row['id_document']))
319 $documents[]= $row['id_document'];
320 return true;
321 }
322 return false;
323 }
324 if ($row['objet'] == 'rubrique') {
325 if (in_array($row['id_objet'], $les_rubriques)) {
326 if (isset($row['id_document']))
327 $documents[]=$row['id_document'];
328 return true;
329 }
330 return false;
331 }
332 }
333
334 if (isset($row['id_document']) AND $row['id_document']) {
335 return array_search($row['id_document'], $documents);
336 }
337 // a la louche pour le reste, mais c'est a peu pres ca.
338 return (isset($row['id_groupe']) OR isset($row['id_mot']) OR isset($row['mime_type']));
339 }
340
341 // Conversion texte -> xml (ajout d'entites)
342 // http://doc.spip.org/@text_to_xml
343 function text_to_xml($string) {
344 static $old = array('&','<','>');
345 static $new = array('&amp;','&lt;','&gt;');
346 return str_replace($old, $new, $string);
347 }
348
349 ?>