3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2011 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
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 \***************************************************************************/
13 if (!defined('_ECRIRE_INC_VERSION')) return;
15 define('_EXPORT_TRANCHES_LIMITE', 200);
16 define('_EXTENSION_PARTIES', '.gz');
18 // http://doc.spip.org/@exec_export_all_args
19 function inc_export_dist($meta)
21 if (!isset($GLOBALS['meta'][$meta])) {
22 include_spip('inc/minipres');
27 list($gz, $archive, $rub, $tables_for_dump, $etape_actuelle, $sous_etape) =
28 unserialize($GLOBALS['meta'][$meta]);
30 // determine upload va aussi initialiser l'index "restreint"
31 $maindir = determine_upload();
32 if (!$GLOBALS['visiteur_session']['restreint'])
34 $dir = sous_repertoire($maindir, $meta);
35 $file = $dir . $archive;
36 $metatable = $meta . '_tables';
38 // Reperer une situation anormale (echec reprise sur interruption)
39 if (!$etape_actuelle AND !$sous_etape) {
40 $l = preg_files($file . ".part_[0-9]+_[0-9]+");
42 spip_log("menage d'une sauvegarde inachevee: " . join(',', $l));
43 foreach($l as $dummy) spip_unlink($dummy);
45 $start = true; // utilise pour faire un premier hit moitie moins long
46 $tables_sauvegardees = array();
47 } else $tables_sauvegardees = isset($GLOBALS['meta'][$metatable])?
unserialize($GLOBALS['meta'][$metatable]):array();
49 // concatenation des fichiers crees a l'appel precedent
50 ramasse_parties($dir, $archive);
51 $all = count($tables_for_dump);
52 if ($etape_actuelle > $all OR !$all){
53 return "end,$gz,$archive,$rub"; // c'est fini !
56 include_spip('inc/minipres');
57 @ini_set
("zlib.output_compression","0"); // pour permettre l'affichage au fur et a mesure
59 echo ( install_debut_html(_T('info_sauvegarde') . " ($all)"));
61 if (!($timeout = ini_get('max_execution_time')*1000));
62 $timeout = 30000; // parions sur une valeur tellement courante ...
63 // le premier hit est moitie moins long car seulement une phase d'ecriture de morceaux
65 // sinon grosse ecriture au 1er hit, puis gros rammassage au deuxieme avec petite ecriture,... ca oscille
66 if ($start) $timeout = round($timeout/2);
68 // Les sauvegardes partielles prennent le temps d'indiquer les logos
69 // Instancier une fois pour toutes, car on va boucler un max.
70 // On complete jusqu'au secteur pour resituer dans l'arborescence)
72 $GLOBALS['chercher_logo'] = charger_fonction('chercher_logo', 'inc',true);
73 $les_rubriques = complete_fils(array($rub));
74 $les_meres = complete_secteurs(array($rub));
76 $GLOBALS['chercher_logo'] = false;
77 $les_rubriques = $les_meres = '';
80 // script de rechargement auto sur timeout
81 $redirect = generer_url_ecrire("export_all");
82 echo http_script("window.setTimeout('location.href=\"".$redirect."\";',$timeout)");
84 echo "<div style='text-align: left'>\n";
86 foreach($tables_for_dump as $table){
87 if ($etape_actuelle > $etape) {
88 // sauter les deja faits, mais rappeler qu'ils sont fait
89 echo ( "\n<br /><strong>".$etape. '. '."</strong>". $tables_sauvegardees[$table]);
92 echo ( "\n<br /><strong>".$etape. '. '. $table."</strong> ");
93 $r = sql_countsel($table);
95 if (!$r) $r = ( _T('texte_vide'));
97 $f = $dir . $archive . '.part_' . sprintf('%03d',$etape);
98 $r = export_objets($table, $sous_etape, $r, $f, $les_rubriques, $les_meres, $meta);
99 $r +
= $sous_etape*_EXPORT_TRANCHES_LIMITE
;
100 // info pas fiable si interruption+partiel
101 if ($rub AND $etape_actuelle > 1) $r = ">= $r";
106 // on utilise l'index comme ca c'est pas grave si on ecrit plusieurs fois la meme
107 $tables_sauvegardees[$table] = "$table ($r)";
108 ecrire_meta($metatable, serialize($tables_sauvegardees),'non');
111 $v = serialize(array($gz, $archive, $rub, $tables_for_dump, $etape,$sous_etape));
112 ecrire_meta($meta, $v,'non');
115 // si Javascript est dispo, anticiper le Time-out
116 echo ("<script language=\"JavaScript\" type=\"text/javascript\">window.setTimeout('location.href=\"$redirect\";',0);</script>\n");
117 echo (install_fin_html());
123 // http://doc.spip.org/@complete_secteurs
124 function complete_secteurs($les_rubriques)
127 foreach($les_rubriques as $r) {
129 $r = sql_getfetsel("id_parent", "spip_rubriques", "id_rubrique=$r");
131 if ((isset($les_rubriques[$r])) OR isset($res[$r]))
140 // http://doc.spip.org/@complete_fils
141 function complete_fils($rubriques)
145 $q = sql_select("id_rubrique", "spip_rubriques", "id_parent IN (".join(',',$r).")");
147 while ($row = sql_fetch($q)) {
148 $r[]= $rubriques[] = $row['id_rubrique'];
156 // Concatenation des tranches
157 // Il faudrait ouvrir une seule fois le fichier, et d'abord sous un autre nom
158 // et sans detruire les tranches: au final renommage+destruction massive pour
159 // prevenir autant que possible un Time-out.
161 // http://doc.spip.org/@ramasse_parties
162 function ramasse_parties($dir, $archive)
164 $files = preg_files($dir . $archive . ".part_[0-9]+_[0-9]+[.gz]?");
168 $but = $dir . $archive;
169 foreach($files as $f) {
171 if (lire_fichier ($f, $contenu)) {
172 if (!ecrire_fichier($but,$contenu,false,false))
173 { $ok = false; break;}
178 return $ok ?
$files_o : false;
182 // Exportation de table SQL au format xml
183 // La constante ci-dessus determine la taille des tranches,
184 // chaque tranche etant copiee immediatement dans un fichier
185 // et son numero memorisee dans le serveur SQL.
186 // En cas d'abandon sur Time-out, le travail pourra ainsi avancer.
187 // Au final, on regroupera les tranches en un seul fichier
188 // et on memorise dans le serveur qu'on va passer a la table suivante.
189 // on prefere ne pas faire le ramassage ici de peur d'etre interrompu
190 // par le timeout au mauvais moment
191 // le ramassage aura lieu en debut de hit suivant,
192 // et ne sera normalement pas interrompu car le temps pour ramasser
193 // est plus court que le temps pour creer les parties
195 // http://doc.spip.org/@export_objets
196 function export_objets($table, $cpt, $total, $filetable, $les_rubriques, $les_meres, $meta) {
197 global $tables_principales;
199 $temp = $filetable . '.temp' . _EXTENSION_PARTIES
;
200 $prim = isset($tables_principales[$table])
201 ?
$tables_principales[$table]['key']["PRIMARY KEY"]
203 $debut = $cpt * _EXPORT_TRANCHES_LIMITE
;
206 while (1){ // on ne connait pas le nb de paquets d'avance
209 $tranche = build_while($debut, $table, $prim, $les_rubriques, $les_meres);
210 // attention: vide ne suffit pas a sortir
211 // car les sauvegardes partielles peuvent parcourir
212 // une table dont la portion qui les concerne sera vide..
214 // on ecrit dans un fichier generique
215 // puis on le renomme pour avoir une operation atomique
216 ecrire_fichier ($temp, join('', $tranche));
217 $f = $filetable . sprintf('_%04d',$cpt) . _EXTENSION_PARTIES
;
218 // le fichier destination peut deja exister
219 // si on sort d'un timeout entre le rename et le ecrire_meta
220 if (file_exists($f)) spip_unlink($f);
222 $effectifs +
= count($tranche);
224 // incrementer le numero de sous-etape
225 // au cas ou une interruption interviendrait
226 $v = unserialize($GLOBALS['meta'][$meta]);
228 ecrire_meta($meta, serialize($v));
229 $debut +
= _EXPORT_TRANCHES_LIMITE
;
230 if ($debut >= $total) {break;}
231 /* pour tester la robustesse de la reprise sur interruption
232 decommenter ce qui suit.
234 spip_log("force interrup $s");
235 include_spip('inc/headers');
236 redirige_par_entete("./?exec=export_all&rub=$rub&x=$s");
246 // Construit la version xml des champs d'une table
248 // http://doc.spip.org/@build_while
249 function build_while($debut, $table, $prim, $les_rubriques, $les_meres) {
250 global $chercher_logo ;
252 // sauver par ordre croissant les tables avec cles primaires simples
253 // sinon les sequences PG seront pertubees a la restauration
255 $result = sql_select('*', $table, '', '', $prim, "$debut," . _EXPORT_TRANCHES_LIMITE
);
258 while ($row = sql_fetch($result)) {
259 if (export_select($row, $les_rubriques, $les_meres)) {
261 if ($chercher_logo) {
262 if ($logo = $chercher_logo($row[$prim], $prim, 'on'))
263 $attributs .= ' on="' . $logo[3] . '"';
264 if ($logo = $chercher_logo($row[$prim], $prim, 'off'))
265 $attributs .= ' off="' . $logo[3] . '"';
268 $string = "<$table$attributs>\n";
269 foreach ($row as $k => $v) {
270 $string .= "<$k>" . text_to_xml($v) . "</$k>\n";
272 $string .= "</$table>\n\n";
280 // dit si Row est exportable,
281 // en particulier quand on se restreint a certaines rubriques
282 // Attention, la table articles doit etre au debut
283 // et la table document_articles avant la table documents
284 // (faudrait blinder, c'est un bug potentiel)
286 // http://doc.spip.org/@export_select
287 function export_select($row, $les_rubriques, $les_meres) {
288 static $articles = array();
289 static $documents = array();
291 if (isset($row['impt']) AND $row['impt'] !='oui') return false;
292 if (!$les_rubriques) return true;
294 // numero de rubrique non determinant pour les forums (0 � 99%)
295 if (isset($row['id_rubrique']) AND $row['id_rubrique']) {
296 if (in_array($row['id_rubrique'], $les_rubriques)) {
297 if (isset($row['id_article']))
298 $articles[] = $row['id_article'];
299 if (isset($row['id_document']))
300 $documents[]=$row['id_document'];
303 if (!in_array($row['id_rubrique'], $les_meres))
305 // la rubrique, mais rien d'autre
306 return (!isset($row['id_article'])
307 AND !isset($row['id_mot'])
308 AND !isset($row['id_document'])
309 AND !isset($row['id_breve']));
311 // dependances d'articles (mots, petitions, signatures et documents)
312 if (isset($row['id_article']) AND $row['id_article']) {
313 if (in_array($row['id_article'], $articles)) {
314 if (isset($row['id_document']))
315 $documents[]= $row['id_document'];
320 if (isset($row['id_objet']) AND isset($row['objet'])) {
321 if ($row['objet'] == 'article') {
322 if (in_array($row['id_objet'], $articles)) {
323 if (isset($row['id_document']))
324 $documents[]= $row['id_document'];
329 if ($row['objet'] == 'rubrique') {
330 if (in_array($row['id_objet'], $les_rubriques)) {
331 if (isset($row['id_document']))
332 $documents[]=$row['id_document'];
339 if (isset($row['id_document']) AND $row['id_document']) {
340 return array_search($row['id_document'], $documents);
342 // a la louche pour le reste, mais c'est a peu pres ca.
343 return (isset($row['id_groupe']) OR isset($row['id_mot']) OR isset($row['mime_type']));
346 // Conversion texte -> xml (ajout d'entites)
347 // http://doc.spip.org/@text_to_xml
348 function text_to_xml($string) {
349 return str_replace(array('&','<','>'), array('&','<','>'), $string);