[SPIP] v3.2.7-->v3.2.9
[lhc/web/www.git] / www / plugins-dist / svp / inc / svp_depoter_distant.php
1 <?php
2
3 /**
4 * Traitement des dépots distants
5 *
6 * Un dépot distant est une liste de paquets que l'on peut télécharger.
7 * Cette liste est donnée par un fichier XML que l'on peut relire
8 * régulièrement pour actualiser nos informations. Effectivement, chaque
9 * paquet (et plugin) décrit est inséré en base de données pour nous
10 * faciliter les recherches.
11 *
12 * @plugin SVP pour SPIP
13 * @license GPL
14 * @package SPIP\SVP\Depots
15 */
16
17 if (!defined("_ECRIRE_INC_VERSION")) {
18 return;
19 }
20 include_spip('inc/plugin');
21 include_spip('inc/svp_phraser');
22
23
24 /**
25 * Ajout d'un dépot et de son contenu (paquets, plugins) dans la base de données
26 *
27 * Si une erreur survient (syntaxe XML incorrecte, pas de plugin dans le dépot),
28 * son texte est placé dans le paramètre $erreur
29 *
30 * @uses svp_phraser_depot()
31 * @uses svp_actualiser_paquets()
32 * @uses svp_base_supprimer_paquets_locaux()
33 * @param string $url
34 * URL du fichier XML de description du dépot
35 * @param string $erreur
36 * Texte d'un éventuel message d'erreur
37 * @return bool
38 * true si le dépot est ajouté correctement, false sinon
39 */
40 function svp_ajouter_depot($url, &$erreur = '') {
41 include_spip('inc/distant');
42
43 // On considere que l'url a deja ete validee (correcte et nouveau depot)
44 $url = trim($url);
45 // Ajout du depot dans la table spip_depots. Les compteurs de paquets et de plugins
46 // sont mis a jour apres le traitement des paquets
47
48 // on recupère le XML
49 $fichier_xml = copie_locale($url, 'modif');
50 if (!$fichier_xml) {
51 $erreur = _T('svp:message_nok_xml_non_recupere', array('fichier' => $url));
52
53 return false;
54 }
55
56 $fichier_xml = _DIR_RACINE . $fichier_xml;
57
58 // Lire les donnees d'un depot de paquets
59 $infos = svp_phraser_depot($fichier_xml);
60 if (!$infos) {
61 $erreur = _T('svp:message_nok_xml_non_conforme', array('fichier' => $url));
62
63 return false;
64 }
65
66 $titre = filtrer_entites($infos['depot']['titre']);
67 $champs = array(
68 'titre' => $titre,
69 'descriptif' => filtrer_entites($infos['depot']['descriptif']),
70 'type' => $infos['depot']['type'],
71 'url_serveur' => $infos['depot']['url_serveur'],
72 'url_brouteur' => $infos['depot']['url_brouteur'],
73 'url_archives' => $infos['depot']['url_archives'],
74 'url_commits' => $infos['depot']['url_commits'],
75 'xml_paquets' => $url,
76 'sha_paquets' => sha1_file($fichier_xml),
77 'nbr_paquets' => 0,
78 'nbr_plugins' => 0,
79 'nbr_autres' => 0
80 );
81
82 // verifier avant l'insertion que le depot n'existe pas deja
83 // car la recuperation pouvant etre longue on risque le probleme en cas de concurrence
84 if (sql_countsel('spip_depots', 'xml_paquets=' . sql_quote($url))) {
85 $erreur = _T('svp:message_nok_depot_deja_ajoute', array('url' => $url));
86
87 return false;
88 } elseif (!$id_depot = sql_insertq('spip_depots', $champs)) {
89 $erreur = _T('svp:message_nok_sql_insert_depot', array('objet' => "$titre ($url)"));
90
91 return false;
92 }
93
94 // Ajout des paquets dans spip_paquets et actualisation des plugins dans spip_plugins
95 $ok = svp_actualiser_paquets($id_depot, $infos['paquets'], $nb_paquets, $nb_plugins, $nb_autres);
96 if (!$ok or ($nb_paquets == 0)) {
97 // Si une erreur s'est produite, on supprime le depot deja insere
98 sql_delete('spip_depots', 'id_depot=' . sql_quote($id_depot));
99 if (!$ok) {
100 $erreur = _T('svp:message_nok_xml_non_conforme', array('fichier' => $url));
101 } else {
102 $erreur = _T('svp:message_nok_aucun_paquet_ajoute', array('url' => $url));
103 }
104
105 return false;
106 }
107
108 // On met à jour le nombre de paquets et de plugins du depot maintenant !
109 sql_updateq('spip_depots',
110 array('nbr_paquets' => $nb_paquets, 'nbr_plugins' => $nb_plugins, 'nbr_autres' => $nb_autres),
111 'id_depot=' . sql_quote($id_depot));
112
113 // On vide les paquets locaux pour mettre a jour leurs donnees relatives au depot
114 // comme les mises a jour disponibles
115 include_spip('inc/svp_depoter_local');
116 svp_actualiser_paquets_locaux(true);
117
118 return true;
119 }
120
121 /**
122 * Suppression d'un dépot et de son contenu (paquets, plugins) dans la base de données
123 *
124 * Cette suppression entraîne des recalcul comme les versions maximales
125 * des plugins téléchargeables qui peuvent changer.
126 *
127 * @uses svp_actualiser_url_plugins()
128 * @uses svp_nettoyer_apres_suppression()
129 * @uses svp_base_supprimer_paquets_locaux()
130 *
131 * @param int $id
132 * Identifiant du dépot
133 * @return bool
134 * false si le dépot n'est pas trouvé, true sinon
135 */
136 function svp_supprimer_depot($id) {
137 $id = intval($id);
138
139 // Pas de depot a cet id ?
140 if (!$id_depot = sql_getfetsel('id_depot', 'spip_depots', 'id_depot=' . sql_quote($id))) {
141 return false;
142 }
143
144 // on calcule les versions max des plugins heberges par le depot
145 $vmax = array();
146
147 if ($resultats = sql_allfetsel('id_plugin, version', 'spip_paquets', 'id_depot=' . sql_quote($id))) {
148 foreach ($resultats as $paquet) {
149 $id_plugin = $paquet['id_plugin'];
150 if (!isset($vmax[$id_plugin])
151 or (spip_version_compare($vmax[$id_plugin], $paquet['version'], '<'))
152 ) {
153 $vmax[$id_plugin] = $paquet['version'];
154 }
155 }
156 }
157
158 // On supprime les paquets heberges par le depot
159 sql_delete('spip_paquets', 'id_depot=' . sql_quote($id_depot));
160
161 // Si on est pas en mode runtime, on utilise surement l'espace public pour afficher les plugins.
162 // Il faut donc verifier que les urls suivent bien la mise à jour
163 // Donc avant de nettoyer la base des plugins du depot ayant disparus on supprime toutes les urls
164 // associees a ce depot : on les recreera apres le nettoyage
165 if (!_SVP_MODE_RUNTIME) {
166 svp_actualiser_url_plugins($id_depot);
167 }
168
169 // Nettoyer les autres relations à ce dépot
170 svp_nettoyer_apres_suppression($id_depot, $vmax);
171
172 // Si on est pas en mode runtime, on utilise surement l'espace public pour afficher les plugins.
173 // Il faut donc s'assurer que les urls suivent bien la mise à jour
174 // - on supprime toutes les urls plugin
175 // - on les regenere pour la liste des plugins mise a jour
176 if (!_SVP_MODE_RUNTIME) {
177 svp_actualiser_url_plugins($id_depot);
178 }
179
180 // On supprime le depot lui-meme
181 sql_delete('spip_depots', 'id_depot=' . sql_quote($id_depot));
182
183 // on supprime les paquets locaux pour reactualisation
184 include_spip('inc/svp_depoter_local');
185 svp_base_supprimer_paquets_locaux();
186
187 return true;
188 }
189
190
191 /**
192 * Nettoyer la base de données après la suppression d'un dépot
193 *
194 * Supprime
195 * - les liens des plugins avec le dépot (table spip_depots_plugins)
196 * - les plugins dont aucun paquet n'est encore hébergé par un dépot restant (table spip_plugins)
197 * Remet à zéro la version maximale des plugins ayant vu leur paquet en version maximale supprimée
198 *
199 * @param int $id_depot
200 * Identifiant du dépot
201 * @param array $vmax
202 * Tableau de la version maximale des plugins du dépot supprimé
203 * Tableau (id_plugin => version maximale)
204 * @return bool
205 * true toujours.
206 **/
207 function svp_nettoyer_apres_suppression($id_depot, $vmax) {
208
209 // On rapatrie la liste des plugins du depot qui servira apres qu'on ait supprime les liens
210 // de la table spip_depots_plugins
211 $liens = sql_allfetsel('id_plugin', 'spip_depots_plugins', 'id_depot=' . sql_quote($id_depot));
212 $plugins_depot = array_map('reset', $liens);
213
214 // On peut donc supprimer tous ces liens *plugins-depots* du depot
215 sql_delete('spip_depots_plugins', 'id_depot=' . sql_quote($id_depot));
216
217 // On verifie pour chaque plugin concerne par la disparition de paquets si c'est la version
218 // la plus elevee qui a ete supprimee.
219 // Si oui, on positionne le vmax a 0, ce qui permettra de remettre a jour le plugin systematiquement
220 // a la prochaine actualisation.
221 // Cette operation est necessaire car on n'impose pas que les informations du plugin soient identiques
222 // pour chaque paquet !!!
223
224 // On insere, en encapsulant pour sqlite...
225 if (sql_preferer_transaction()) {
226 sql_demarrer_transaction();
227 }
228
229 if ($resultats = sql_allfetsel('id_plugin, vmax', 'spip_plugins', sql_in('id_plugin', $plugins_depot))) {
230 foreach ($resultats as $plugin) {
231 if (spip_version_compare($plugin['vmax'], $vmax[$plugin['id_plugin']], '=')) {
232 sql_updateq('spip_plugins', array('vmax' => ''), 'id_plugin=' . sql_quote($plugin['id_plugin']));
233 }
234 }
235 }
236
237 if (sql_preferer_transaction()) {
238 sql_terminer_transaction();
239 }
240
241 // Maintenant on calcule la liste des plugins du depot qui ne sont pas heberges
242 // par un autre depot => donc a supprimer
243 // - Liste de tous les plugins encore lies a un autre depot
244 // tous les plugins correspondants aux anciens paquets
245 $plugins_restants = sql_allfetsel('DISTINCT(id_plugin)', 'spip_paquets', sql_in('id_plugin', $plugins_depot));
246 $plugins_restants = array_map('array_shift', $plugins_restants);
247
248 // - L'intersection des deux tableaux renvoie les plugins a supprimer
249 $plugins_a_supprimer = array_diff($plugins_depot, $plugins_restants);
250
251 // On supprimer les plugins identifies
252 sql_delete('spip_plugins', sql_in('id_plugin', $plugins_a_supprimer));
253
254 return true;
255 }
256
257
258 /**
259 * Actualisation des plugins d'un dépot déjà crée
260 *
261 * Actualise les informations uniquement si la signature du fichier
262 * XML de description du dépot a changé
263 *
264 * @uses svp_actualiser_maj_version()
265 * @uses svp_actualiser_paquets()
266 * @uses svp_phraser_depot()
267 * @param int $id
268 * Identifiant du dépot
269 * @return bool
270 * false si erreur, true sinon
271 */
272 function svp_actualiser_depot($id) {
273 include_spip('inc/distant');
274
275 $id = intval($id);
276
277 // pas de depot a cet id ?
278 if (!$depot = sql_fetsel('*', 'spip_depots', 'id_depot=' . sql_quote($id))) {
279 return false;
280 }
281
282 $fichier_xml = _DIR_RACINE . copie_locale($depot['xml_paquets'], 'modif');
283
284 $sha = sha1_file($fichier_xml);
285
286 if ($depot['sha_paquets'] == $sha) {
287 // Le fichier n'a pas change (meme sha1) alors on ne fait qu'actualiser la date
288 // de mise a jour du depot en mettant a jour *inutilement* le sha1
289 spip_log('Aucune modification du fichier XML, actualisation non declenchee - id_depot = ' . $depot['id_depot'],
290 'svp_actions.' . _LOG_INFO);
291 sql_replace('spip_depots', array_diff_key($depot, array('maj' => '')));
292 } else {
293
294 // Le fichier a bien change il faut actualiser tout le depot
295 $infos = svp_phraser_depot($fichier_xml);
296
297 if (!$infos) {
298 return false;
299 }
300
301 // On actualise les paquets dans spip_paquets en premier lieu.
302 // Lors de la mise a jour des paquets, les plugins aussi sont actualises
303 $ok = svp_actualiser_paquets($depot['id_depot'], $infos['paquets'],
304 $nb_paquets, $nb_plugins, $nb_autres);
305
306 // apres la mise a jour des paquets d'un depot, on actualise les informations des paquets locaux
307 // principalement l'info "maj_version" indiquant s'il existe un paquet plus recent
308 include_spip('inc/svp_depoter_local');
309 svp_actualiser_maj_version();
310
311 if ($ok) {
312 // On met à jour :
313 // -- les infos ne pouvant pas etre editees par le formulaire d'edition
314 // d'un depot et extraites du xml
315 // -- le nombre de paquets et de plugins du depot ainsi que le nouveau sha1
316 // ce qui aura pour effet d'actualiser la date de mise a jour
317 $champs = array(
318 'url_serveur' => $infos['depot']['url_serveur'],
319 'url_brouteur' => $infos['depot']['url_brouteur'],
320 'url_archives' => $infos['depot']['url_archives'],
321 'url_commits' => $infos['depot']['url_commits'],
322 'nbr_paquets' => $nb_paquets,
323 'nbr_plugins' => $nb_plugins,
324 'nbr_autres' => $nb_autres,
325 'sha_paquets' => $sha
326 );
327 sql_updateq('spip_depots', $champs, 'id_depot=' . sql_quote($depot['id_depot']));
328 }
329 }
330
331 return true;
332 }
333
334
335 /**
336 * Actualisation de la table des paquets pour le dépot choisi
337 *
338 * Enlève de la base les paquets du dépots qui ne sont plus présents
339 * dans la description du XML. Ajoute ou met à jour les autres.
340 *
341 * @uses svp_supprimer_plugins_orphelins()
342 * @uses svp_corriger_vmax_plugins()
343 * @uses svp_completer_plugins()
344 * @uses eclater_plugin_paquet()
345 * @uses svp_inserer_multi()
346 * @uses svp_completer_plugins_depot()
347 * @uses svp_actualiser_url_plugins()
348 *
349 * @param int $id_depot
350 * Identifiant du dépot
351 * @param array $paquets
352 * Tableau des paquets extraits du fichier XML
353 * L'index est le nom de l'archive (xxxx.zip) et le contenu est
354 * un tableau à deux entrées :
355 * - Index 'plugin' : le tableau des infos du plugin
356 * - Index 'file' : le nom de l'archive .zip
357 * @param int $nb_paquets
358 * Nombre de paquets réellement inserés dans la base
359 * @param int $nb_plugins
360 * Nombre de plugins parmi les paquets inserés
361 * @param int $nb_autres
362 * Nombre de contributions non issues de plugin parmi les paquets inserés
363 * @return bool
364 * false si aucun dépot ou paquets, true sinon
365 */
366 function svp_actualiser_paquets($id_depot, $paquets, &$nb_paquets, &$nb_plugins, &$nb_autres) {
367
368 // Initialisation des compteurs
369 $nb_paquets = 0;
370 $nb_plugins = 0;
371 $nb_autres = 0;
372
373 // Si aucun depot ou aucun paquet on renvoie une erreur
374 if ((!$id_depot) or (!is_array($paquets))) {
375 return false;
376 }
377
378 // On initialise l'url de base des logos du depot et son type afin de
379 // calculer l'url complete de chaque logo
380 $select = array('url_archives', 'type');
381 $depot = sql_fetsel($select, 'spip_depots', 'id_depot=' . sql_quote($id_depot));
382
383
384 // On supprime tous les paquets du depot
385 // qui ont ete evacues, c'est a dire ceux dont les signatures
386 // ne correspondent pas aux nouveaux...
387 // et on retablit les vmax des plugins restants...
388 $signatures = array();
389 foreach ($paquets as $_paquet) {
390 $signatures[] = $_paquet['md5'];
391 }
392
393 // tous les paquets du depot qui ne font pas parti des signatures
394 $anciens_paquets = sql_allfetsel('id_paquet', 'spip_paquets',
395 array('id_depot=' . sql_quote($id_depot), sql_in('signature', $signatures, 'NOT')));
396 $anciens_paquets = array_map('array_shift', $anciens_paquets);
397
398 // pour ces vieux paquets, on les nettoie de la base
399 if ($anciens_paquets) {
400 // tous les plugins correspondants aux anciens paquets
401 $anciens_plugins = sql_allfetsel('pl.id_plugin', array('spip_plugins AS pl', 'spip_paquets AS pa'),
402 array('pl.id_plugin=pa.id_plugin', sql_in('pa.id_paquet', $anciens_paquets)));
403 $anciens_plugins = array_map('array_shift', $anciens_plugins);
404
405 // suppression des anciens paquets
406 sql_delete('spip_paquets', sql_in('id_paquet', $anciens_paquets));
407 // suppressions des liaisons depots / anciens plugins
408 // on enlève la liaison lorsqu'il n'y a plus aucun paquet lie a un des plugins qui ont vu un paquet enlevé
409
410 // liste des plugins qui ont encore des paquets dans ce depot
411 $plugins_restants = sql_allfetsel('pl.id_plugin',
412 array('spip_plugins AS pl', 'spip_paquets AS pa'),
413 array(
414 sql_in('pl.id_plugin', $anciens_plugins),
415 'pl.id_plugin=pa.id_plugin',
416 'pa.id_depot=' . sql_quote($id_depot)
417 ));
418 $plugins_restants = array_map('array_shift', $plugins_restants);
419 // par opposition, on retrouve ceux qui n'en ont plus...
420 $plugins_supprimes = array_diff($anciens_plugins, $plugins_restants);
421 sql_delete('spip_depots_plugins',
422 array('id_depot=' . sql_quote($id_depot), sql_in('id_plugin', $plugins_supprimes)));
423 unset($plugins_restants, $plugins_supprimes);
424
425 // supprimer les plugins orphelins
426 include_spip('inc/svp_depoter_local');
427 svp_supprimer_plugins_orphelins($anciens_plugins);
428
429 // corriger les vmax des plugins
430 svp_corriger_vmax_plugins($anciens_plugins);
431
432 // corriger les compats, branches aussi
433 svp_completer_plugins($anciens_plugins);
434 }
435
436 // on ne garde que les paquets qui ne sont pas presents dans la base
437 $signatures = sql_allfetsel('signature', 'spip_paquets', 'id_depot=' . sql_quote($id_depot));
438 $signatures = array_map('array_shift', $signatures);
439 foreach ($paquets as $cle => $_infos) {
440 if (in_array($_infos['md5'], $signatures)) {
441 unset($paquets[$cle]);
442 }
443 }
444
445 // tableaux d'actions
446 $insert_paquets = array();
447 $insert_plugins = array();
448 $insert_contribs = array();
449 $prefixes = array(); // prefixe => id_plugin
450
451 // On met a jour ou on cree chaque paquet a partir du contenu du fichier xml
452 // On ne fait pas cas de la compatibilite avec la version de SPIP installee
453 // car l'operation doit permettre de collecter tous les paquets
454 foreach ($paquets as $_archive => $_infos) {
455
456 $insert_paquet = array();
457 // On initialise les informations specifiques au paquet :
458 // l'id du depot et les infos de l'archive
459 $insert_paquet['id_depot'] = $id_depot;
460 $insert_paquet['nom_archive'] = $_archive;
461 $insert_paquet['nbo_archive'] = $_infos['size'];
462 $insert_paquet['maj_archive'] = date('Y-m-d H:i:s', $_infos['date']);
463 $insert_paquet['src_archive'] = $_infos['source'];
464 $insert_paquet['date_modif'] = $_infos['last_commit'];
465 // On serialise le tableau des traductions par module
466 $insert_paquet['traductions'] = serialize($_infos['traductions']);
467 // On ajoute la signature
468 $insert_paquet['signature'] = $_infos['md5'];
469
470 // On verifie si le paquet est celui d'un plugin ou pas
471 // -- Les traitements du XML dependent de la DTD utilisee
472 // Formatage des informations extraites du plugin pour insertion dans la base SVP
473 $formater = charger_fonction('preparer_sql_' . $_infos['dtd'], 'plugins');
474 if ($champs_aplat = $formater($_infos['plugin'])) {
475 // Eclater les champs recuperes en deux sous tableaux, un par table (plugin, paquet)
476 $champs = eclater_plugin_paquet($champs_aplat);
477
478 $paquet_plugin = true;
479 // On complete les informations du paquet et du plugin
480 $insert_paquet = array_merge($insert_paquet, $champs['paquet']);
481 $insert_plugin = $champs['plugin'];
482 // Le logo est normalement fourni dans les infos de zip
483 if (!empty($_infos['logo'])) {
484 $insert_paquet['logo'] = $depot['url_archives'] . '/' . $_infos['logo'];
485 }
486 elseif ($insert_paquet['logo']) {
487 // Sinon on construit l'url complete du logo
488 // Le logo est maintenant disponible a la meme adresse que le zip et porte le nom du zip.
489 // Son extension originale est conservee
490 $insert_paquet['logo'] = $depot['url_archives'] . '/'
491 . preg_replace(",\.zip$,i", "", $insert_paquet['nom_archive']) . '.'
492 . pathinfo($insert_paquet['logo'], PATHINFO_EXTENSION);
493 }
494
495 // On loge l'absence de categorie ou une categorie erronee et on positionne la categorie
496 // par defaut "aucune"
497 // Provisoire tant que la DTD n'est pas en fonction
498 if (!$insert_plugin['categorie']) {
499 spip_log("Categorie absente dans le paquet issu de <" . $insert_paquet['src_archive'] .
500 "> du depot <" . $insert_paquet['id_depot'] . ">\n", 'svp_paquets.' . _LOG_INFO_IMPORTANTE);
501 $insert_plugin['categorie'] = 'aucune';
502 } else {
503 $svp_categories = $GLOBALS['categories_plugin'];
504 if (!in_array($insert_plugin['categorie'], $svp_categories)) {
505 spip_log("Categorie &#107;" . $insert_plugin['categorie'] . "&#108; incorrecte dans le paquet issu de <" . $insert_paquet['src_archive'] .
506 "> du depot <" . $insert_paquet['id_depot'] . ">\n", 'svp_paquets.' . _LOG_INFO_IMPORTANTE);
507 $insert_plugin['categorie'] = 'aucune';
508 }
509 }
510 } else {
511 $paquet_plugin = false;
512 }
513 // On teste l'existence du paquet dans la base avec les champs
514 // id_depot, nom_archive et src_archive pour être sur de l'unicité.
515 // - si le paquet n'existe pas, on le crée,
516 // - sinon (et ça ne devrait pas arriver), on ne fait qu'un update
517 if (!$paquet = sql_fetsel('*', 'spip_paquets', array(
518 'id_depot=' . sql_quote($insert_paquet['id_depot']),
519 'nom_archive=' . sql_quote($insert_paquet['nom_archive']),
520 'src_archive=' . sql_quote($insert_paquet['src_archive'])
521 ))
522 ) {
523 // Le paquet n'existe pas encore en base de donnees
524 // ------------------------------------------------
525
526 // On positionne la date de creation a celle du dernier commit ce qui est bien le cas
527 $insert_paquet['date_crea'] = $insert_paquet['date_modif'];
528
529 // Les collisions ne sont possibles que si on ajoute un nouveau paquet
530 $collision = false;
531
532 if ($paquet_plugin) {
533 // On est en presence d'un PLUGIN
534 // ------------------------------
535 // On evite les doublons de paquet
536 // Pour determiner un doublon on verifie actuellement :
537 // - le prefixe
538 // - la version du paquet et de la base
539 // - l'etat
540 $where = array(
541 't1.id_plugin=t2.id_plugin',
542 't1.version=' . sql_quote($insert_paquet['version']),
543 't1.version_base=' . sql_quote($insert_paquet['version_base']),
544 't1.etatnum=' . sql_quote($insert_paquet['etatnum']),
545 't1.id_depot>' . intval(0),
546 't2.prefixe=' . sql_quote($insert_plugin['prefixe'])
547 );
548 if (!$id_paquet = sql_getfetsel('t1.id_paquet', 'spip_paquets AS t1, spip_plugins AS t2', $where)) {
549 // On traite d'abord le plugin du paquet pour recuperer l'id_plugin
550 // On rajoute le plugin dans la table spip_plugins si celui-ci n'y est pas encore ou on recupere
551 // l'id si il existe deja et on le met a jour si la version du paquet est plus elevee
552
553 $plugin = sql_fetsel('id_plugin, vmax', 'spip_plugins',
554 array('prefixe=' . sql_quote($insert_plugin['prefixe'])));
555 if (!$plugin and !array_key_exists($insert_plugin['prefixe'], $insert_plugins)) {
556 $insert_plugins[$insert_plugin['prefixe']] = array_merge($insert_plugin,
557 array('vmax' => $insert_paquet['version']));
558 } else {
559 if ($plugin) {
560 $id_plugin = $plugin['id_plugin'];
561 $prefixes[$insert_plugin['prefixe']] = $id_plugin;
562 }
563 if (array_key_exists($insert_plugin['prefixe'], $insert_plugins)
564 and (spip_version_compare($insert_plugins[$insert_plugin['prefixe']]['vmax'], $insert_paquet['version'],
565 '<='))
566 ) {
567 // attribuer au plugin le nom et le slogan du paquet le plus à jour
568 $insert_plugins[$insert_plugin['prefixe']]['nom'] = $insert_plugin['nom'];
569 $insert_plugins[$insert_plugin['prefixe']]['slogan'] = $insert_plugin['slogan'];
570 $insert_plugins[$insert_plugin['prefixe']]['vmax'] = $insert_paquet['version'];
571 }
572 }
573
574 // On traite maintenant le paquet connaissant l'id du plugin
575 // temporaire qui sera supprime lors de la connaissance de l'id_paquet
576 $insert_paquet['prefixe'] = $insert_plugin['prefixe'];
577 $insert_paquets[] = $insert_paquet;
578 } else {
579 $collision = true;
580 }
581 } else {
582 // On est en presence d'une CONTRIBUTION NON PLUGIN
583 // ------------------------------------------------
584 $where = array(
585 'id_depot=' . sql_quote($insert_paquet['id_depot']),
586 'nom_archive=' . sql_quote($insert_paquet['nom_archive'])
587 );
588 if (!$id_paquet = sql_getfetsel('id_paquet', 'spip_paquets', $where)) {
589 // Ce n'est pas un plugin, donc id_plugin=0 et toutes les infos plugin sont nulles
590 $insert_paquet['id_plugin'] = 0;
591 $insert_contribs[] = $insert_paquet;
592 } else {
593 $collision = true;
594 }
595 }
596 // On loge le paquet ayant ete refuse dans un fichier a part afin de les verifier
597 // apres coup
598 if ($collision) {
599 spip_log("Collision avec le paquet <" . $insert_paquet['nom_archive'] .
600 " / " . $insert_paquet['src_archive'] . "> du depot <" . $insert_paquet['id_depot'] . ">\n",
601 'svp_paquets.' . _LOG_INFO_IMPORTANTE);
602 }
603 } else {
604 // Le paquet existe deja en base de donnees
605 // ----------------------------------------
606
607 // On ne devrait plus arriver ICI...
608 // Code obsolete ?
609 spip_log('!!!!!! Passage dans code obsolete (svp/svp_depoter_distant)', 'depoter');
610
611 // on effectue les traitements en attente
612 // pour que les updates soient corrects
613 svp_inserer_multi($insert_plugins, $insert_paquets, $insert_contribs, $prefixes);
614
615
616 // On met a jour le paquet en premier lieu qu'il soit un plugin ou une contribution
617 sql_updateq('spip_paquets', $insert_paquet,
618 'id_paquet=' . sql_quote($paquet['id_paquet']));
619
620 }
621 }
622
623 // on effectue les traitements en attente
624 // pour que les updates soient corrects
625 svp_inserer_multi($insert_plugins, $insert_paquets, $insert_contribs, $prefixes);
626
627 // On rajoute le plugin comme heberge par le depot si celui-ci n'est pas encore enregistre comme tel
628 $ids = sql_allfetsel('p.id_plugin',
629 array('spip_plugins AS p', 'spip_depots_plugins AS dp'),
630 array('p.id_plugin=dp.id_plugin', 'dp.id_depot=' . sql_quote($id_depot)));
631 $ids = array_map('array_shift', $ids);
632
633 // inserer les liens avec le depots
634 $insert_dp = array();
635 $news_id = array_diff(array_values($prefixes), $ids);
636 foreach ($news_id as $id) {
637 $insert_dp[] = array('id_depot' => $id_depot, 'id_plugin' => $id);
638 }
639 if ($insert_dp) {
640 sql_insertq_multi('spip_depots_plugins', $insert_dp);
641 }
642
643 // on recalcul les vmax des plugins de ce depot.
644 svp_corriger_vmax_plugins(array_values($prefixes));
645
646 // On compile maintenant certaines informations des paquets mis a jour dans les plugins
647 // (date de creation, date de modif, version spip...)
648 svp_completer_plugins_depot($id_depot);
649
650 // Si on est pas en mode runtime, on utilise surement l'espace public pour afficher les plugins.
651 // Il faut donc s'assurer que les urls suivent bien la mise à jour
652 // - on supprime toutes les urls plugin
653 // - on les regenere pour la liste des plugins mise a jour
654 if (!_SVP_MODE_RUNTIME) {
655 svp_actualiser_url_plugins();
656 }
657
658 // Calcul des compteurs de paquets, plugins et contributions
659 $nb_paquets = sql_countsel('spip_paquets', 'id_depot=' . sql_quote($id_depot));
660 $nb_plugins = sql_countsel('spip_depots_plugins', 'id_depot=' . sql_quote($id_depot));
661 $nb_autres = sql_countsel('spip_paquets', array('id_depot=' . sql_quote($id_depot), 'id_plugin=0'));
662
663 return true;
664 }
665
666
667 /**
668 * Insertion en masse de plugins ou de paquets.
669 *
670 * Les paquets peuvent de pas avoir d'info "prefixe" (à transformer en id_plugin)
671 * lorsqu'ils ne proviennent pas de plugin (squelettes...)
672 *
673 * @param array $insert_plugins
674 * Tableau de description de plugins.
675 * Une description est un tableau de couples (colonne sql => valeur)
676 * pour l'insertion en base de données.
677 * @param array $insert_paquets
678 * Tableau de description de paquets.
679 * Une description est un tableau de couples (colonne sql => valeur)
680 * pour l'insertion en base de données.
681 * @param array $insert_contribs
682 * Tableau de description de paquets (contributions non plugins).
683 * Une description est un tableau de couples (colonne sql => valeur)
684 * pour l'insertion en base de données.
685 * @param array $prefixes
686 * Couples de relation (préfixe de plugin => identifiant de plugin) connues,
687 * pour limiter les accès SQL.
688 * @return void
689 **/
690 function svp_inserer_multi(&$insert_plugins, &$insert_paquets, &$insert_contribs, &$prefixes) {
691
692 if (count($insert_plugins)) {
693 sql_insertq_multi('spip_plugins', $insert_plugins);
694 $insert_plugins = array();
695 }
696
697 if (count($insert_paquets)) {
698
699 // on cherche tous les id_plugin/prefixe que l'on a à récuperer
700 // en une seule requete
701 $prefixes_manquants = array();
702 foreach ($insert_paquets as $p) {
703 // on ne connait que le prefixe
704 if (isset($p['prefixe']) and !isset($prefixes[$p['prefixe']])) {
705 $prefixes_manquants[] = $p['prefixe'];
706 }
707 }
708
709 // recuperer les nouveaux prefixes :
710 $new = sql_allfetsel(array('prefixe', 'id_plugin'), 'spip_plugins', sql_in('prefixe', $prefixes_manquants));
711 foreach ($new as $p) {
712 $prefixes[$p['prefixe']] = $p['id_plugin'];
713 }
714
715 // inserer les id_plugin dans les paquets a inserer
716 // inserer le prefixe dans le paquet (pour raccourcis de jointures)
717 foreach ($insert_paquets as $c => $p) {
718 if (isset($p['prefixe'])) {
719 $insert_paquets[$c]['id_plugin'] = $prefixes[$insert_paquets[$c]['prefixe']];
720 } else {
721 $insert_paquets[$c]['prefixe'] = array_search($p['id_plugin'], $prefixes);
722 }
723 }
724
725 // on insere tout !
726 sql_insertq_multi('spip_paquets', $insert_paquets);
727 $insert_paquets = array();
728 }
729
730 // les contribs n'ont pas le même nombre de champs dans les insertions
731 // et n'ont pas de plugin rattachés.
732 if (count($insert_contribs)) {
733 sql_insertq_multi('spip_paquets', $insert_contribs);
734 $insert_contribs = array();
735 }
736 }
737
738 /**
739 * Complète les informations des plugins contenus dans un depot
740 * en compilant certaines informations (compatibilités, dates, branches)
741 *
742 * @uses svp_completer_plugins()
743 * @param int $id_depot
744 * Identifiant du depot à actualiser
745 **/
746 function svp_completer_plugins_depot($id_depot) {
747 // On limite la revue des paquets a ceux des plugins heberges par le depot en cours d'actualisation
748 $ids_plugins = sql_allfetsel('id_plugin', 'spip_depots_plugins', array('id_depot=' . sql_quote($id_depot)));
749 $ids_plugins = array_map('reset', $ids_plugins);
750 if ($ids_plugins) {
751 svp_completer_plugins($ids_plugins);
752 }
753 }
754
755 /**
756 * Complète les informations des plugins, d'une liste de plugins donnés,
757 * en compilant certaines informations (compatibilités, dates, branches)
758 *
759 * @uses compiler_branches_spip()
760 * @param array $ids_plugin
761 * Liste d'identifiants de plugins
762 * @return bool
763 * false si rien à faire, true sinon
764 **/
765 function svp_completer_plugins($ids_plugin) {
766
767 if (!$ids_plugin) {
768 return false;
769 }
770
771 include_spip('inc/svp_outiller');
772
773 // -- on recupere tous les paquets associes aux plugins indiques et on compile les infos
774 if ($resultats = sql_allfetsel('id_plugin, compatibilite_spip, date_crea, date_modif', 'spip_paquets',
775 array(sql_in('id_plugin', $ids_plugin), 'id_depot>' . intval(0)), '', 'id_plugin')
776 ) {
777
778 $plugin_en_cours = 0;
779 $inserts = array();
780 $complements = array('compatibilite_spip' => '', 'branches_spip' => '', 'date_crea' => 0, 'date_modif' => 0);
781
782 foreach ($resultats as $paquet) {
783 // On finalise le plugin en cours et on passe au suivant
784 if ($plugin_en_cours != $paquet['id_plugin']) {
785 // On met a jour le plugin en cours
786 if ($plugin_en_cours) {
787 // On deduit maintenant les branches de la compatibilite globale
788 $complements['branches_spip'] = compiler_branches_spip($complements['compatibilite_spip']);
789 $inserts[$plugin_en_cours] = $complements;
790 }
791 // On passe au plugin suivant
792 $plugin_en_cours = $paquet['id_plugin'];
793 $complements = array('compatibilite_spip' => '', 'branches_spip' => '', 'date_crea' => 0, 'date_modif' => 0);
794 }
795
796 // On compile les compléments du plugin avec le paquet courant sauf les branches
797 // qui sont deduites en fin de compilation de la compatibilite
798 if ($paquet['date_modif'] > $complements['date_modif']) {
799 $complements['date_modif'] = $paquet['date_modif'];
800 }
801 if (($complements['date_crea'] === 0)
802 or ($paquet['date_crea'] < $complements['date_crea'])
803 ) {
804 $complements['date_crea'] = $paquet['date_crea'];
805 }
806 if ($paquet['compatibilite_spip']) {
807 if (!$complements['compatibilite_spip']) {
808 $complements['compatibilite_spip'] = $paquet['compatibilite_spip'];
809 } else {
810 $complements['compatibilite_spip'] = fusionner_intervalles($paquet['compatibilite_spip'],
811 $complements['compatibilite_spip']);
812 }
813 }
814 }
815 // On finalise le dernier plugin en cours
816 $complements['branches_spip'] = compiler_branches_spip($complements['compatibilite_spip']);
817 $inserts[$plugin_en_cours] = $complements;
818
819 // On insere, en encapsulant pour sqlite...
820 if (sql_preferer_transaction()) {
821 sql_demarrer_transaction();
822 }
823
824 foreach ($inserts as $id_plugin => $complements) {
825 sql_updateq('spip_plugins', $complements, 'id_plugin=' . intval($id_plugin));
826 }
827
828 if (sql_preferer_transaction()) {
829 sql_terminer_transaction();
830 }
831
832 }
833
834 return true;
835 }
836
837
838 /**
839 * Recrée toutes les URLs propres de plugin
840 *
841 * Supprime toutes les urls de plugin de la table spip_urls puis les régénère.
842 *
843 * @return int
844 * Nombre d'URLs de plugin régénérées
845 **/
846 function svp_actualiser_url_plugins() {
847 $nb_plugins = 0;
848
849 // On supprime toutes les urls de plugin
850 sql_delete('spip_urls', array('type=\'plugin\''));
851
852 // On recupere les ids des plugins et on regenere les urls
853 if ($ids_plugin = sql_allfetsel('id_plugin', 'spip_plugins')) {
854 $ids_plugin = array_map('reset', $ids_plugin);
855 $nb_plugins = count($ids_plugin);
856
857 foreach ($ids_plugin as $_id) {
858 generer_url_entite($_id, 'plugin', '', '', true);
859 }
860 }
861
862 return $nb_plugins;
863 }
864
865 /**
866 * Éclate une description de paquet issu du XML du dépot en deux parties,
867 * une pour le plugin, l'autre pour le paquet
868 *
869 * Sépare en deux une description de champs désignant un paquet, en extrayant :
870 * - la partie plugin, soit ce qui peut être propre à plusieurs paquets.
871 * On trouve dedans le prefixe, nom, slogan, catégorie, tags
872 * - la partie paquet, soit ce qui est propre à ce conteneur là. On trouve
873 * dedans entre autres la description, la version, la compatibilité
874 * à SPIP, les dépendances, etc...
875 *
876 * @param array $champs_aplat
877 * Couples (clé => valeur) d'un paquet issu de l'analyse XML du dépot
878 * @return array
879 * Tableau de 2 index :
880 * - Index 'plugin' : couples (clé=>valeur) relatives au plugin
881 * - Index 'paquet' : couples (clé=>valeur) spécifiques au paquet
882 **/
883 function eclater_plugin_paquet($champs_aplat) {
884 return array(
885 'plugin' => array(
886 'prefixe' => $champs_aplat['prefixe'],
887 'nom' => $champs_aplat['nom'],
888 'slogan' => $champs_aplat['slogan'],
889 'categorie' => $champs_aplat['categorie'],
890 'tags' => $champs_aplat['tags'],
891 ),
892 'paquet' => array(
893 'logo' => $champs_aplat['logo'],
894 'description' => $champs_aplat['description'],
895 'auteur' => $champs_aplat['auteur'],
896 'credit' => $champs_aplat['credit'],
897 'version' => $champs_aplat['version'],
898 'version_base' => $champs_aplat['version_base'],
899 'compatibilite_spip' => $champs_aplat['compatibilite_spip'],
900 'branches_spip' => $champs_aplat['branches_spip'],
901 'etat' => $champs_aplat['etat'],
902 'etatnum' => $champs_aplat['etatnum'],
903 'licence' => $champs_aplat['licence'],
904 'copyright' => $champs_aplat['copyright'],
905 'lien_doc' => $champs_aplat['lien_doc'],
906 'lien_demo' => $champs_aplat['lien_demo'],
907 'lien_dev' => $champs_aplat['lien_dev'],
908 'dependances' => $champs_aplat['dependances'],
909 'procure' => $champs_aplat['procure'],
910 )
911 );
912 }
913
914
915 /**
916 * Détermine la version max de chaque plugin, c'est à dire
917 * la version maxi d'un des paquets qui lui est lié.
918 *
919 * @param array $plugins Liste d'identifiant de plugins
920 **/
921 function svp_corriger_vmax_plugins($plugins) {
922 // tous les plugins encore lies a des depots (hors local)...
923 // la vmax est a retablir...
924 if ($plugins) {
925 $p = sql_allfetsel('DISTINCT(p.id_plugin)',
926 array('spip_plugins AS p', 'spip_paquets AS pa'),
927 array(sql_in('p.id_plugin', $plugins), 'p.id_plugin=pa.id_plugin', 'pa.id_depot>' . intval(0)));
928 $p = array_map('array_shift', $p);
929
930 // pour les autres, on la fixe correctement
931
932 // On insere, en encapsulant pour sqlite...
933 if (sql_preferer_transaction()) {
934 sql_demarrer_transaction();
935 }
936
937 foreach ($p as $id_plugin) {
938 $vmax = '';
939 if ($pa = sql_allfetsel('version', 'spip_paquets', array('id_plugin=' . $id_plugin, 'id_depot>' . intval(0)))) {
940 foreach ($pa as $v) {
941 if (spip_version_compare($v['version'], $vmax, '>')) {
942 $vmax = $v['version'];
943 }
944 }
945 }
946 sql_updateq('spip_plugins', array('vmax' => $vmax), 'id_plugin=' . intval($id_plugin));
947 }
948
949 if (sql_preferer_transaction()) {
950 sql_terminer_transaction();
951 }
952 }
953 }