svp_depoter_distant.php 33 KB

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