You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

981 lines
34 KiB

<?php
/**
* Traitement des dépots distants
*
* Un dépot distant est une liste de paquets que l'on peut télécharger.
* Cette liste est donnée par un fichier XML que l'on peut relire
* régulièrement pour actualiser nos informations. Effectivement, chaque
* paquet (et plugin) décrit est inséré en base de données pour nous
* faciliter les recherches.
*
* @plugin SVP pour SPIP
* @license GPL
* @package SPIP\SVP\Depots
*/
if (!defined('_ECRIRE_INC_VERSION')) {
return;
}
include_spip('inc/plugin');
include_spip('inc/svp_phraser');
/**
* Ajout d'un dépot et de son contenu (paquets, plugins) dans la base de données
*
* Si une erreur survient (syntaxe XML incorrecte, pas de plugin dans le dépot),
* son texte est placé dans le paramètre $erreur
*
* @uses svp_phraser_depot()
* @uses svp_actualiser_paquets()
* @uses svp_base_supprimer_paquets_locaux()
* @param string $url
* URL du fichier XML de description du dépot
* @param string $erreur
* Texte d'un éventuel message d'erreur
* @return bool
* true si le dépot est ajouté correctement, false sinon
*/
function svp_ajouter_depot($url, &$erreur = '') {
include_spip('inc/distant');
// On considere que l'url a deja ete validee (correcte et nouveau depot)
$url = trim($url);
// Ajout du depot dans la table spip_depots. Les compteurs de paquets et de plugins
// sont mis a jour apres le traitement des paquets
// on recupère le XML
$fichier_xml = copie_locale($url, 'modif');
if (!$fichier_xml) {
$erreur = _T('svp:message_nok_xml_non_recupere', ['fichier' => $url]);
return false;
}
$fichier_xml = _DIR_RACINE . $fichier_xml;
// Lire les donnees d'un depot de paquets
$infos = svp_phraser_depot($fichier_xml);
if (!$infos) {
$erreur = _T('svp:message_nok_xml_non_conforme', ['fichier' => $url]);
return false;
}
$titre = filtrer_entites($infos['depot']['titre']);
$champs = [
'titre' => $titre,
'descriptif' => filtrer_entites($infos['depot']['descriptif']),
'type' => $infos['depot']['type'],
'url_serveur' => $infos['depot']['url_serveur'],
'url_brouteur' => $infos['depot']['url_brouteur'],
'url_archives' => $infos['depot']['url_archives'],
'url_commits' => $infos['depot']['url_commits'],
'xml_paquets' => $url,
'sha_paquets' => sha1_file($fichier_xml),
'nbr_paquets' => 0,
'nbr_plugins' => 0,
'nbr_autres' => 0
];
// verifier avant l'insertion que le depot n'existe pas deja
// car la recuperation pouvant etre longue on risque le probleme en cas de concurrence
if (sql_countsel('spip_depots', 'xml_paquets=' . sql_quote($url))) {
$erreur = _T('svp:message_nok_depot_deja_ajoute', ['url' => $url]);
return false;
} elseif (!$id_depot = sql_insertq('spip_depots', $champs)) {
$erreur = _T('svp:message_nok_sql_insert_depot', ['objet' => "$titre ($url)"]);
return false;
}
// Ajout des paquets dans spip_paquets et actualisation des plugins dans spip_plugins
$ok = svp_actualiser_paquets($id_depot, $infos['paquets'], $nb_paquets, $nb_plugins, $nb_autres);
if (!$ok or ($nb_paquets == 0)) {
// Si une erreur s'est produite, on supprime le depot deja insere
sql_delete('spip_depots', 'id_depot=' . sql_quote($id_depot));
if (!$ok) {
$erreur = _T('svp:message_nok_xml_non_conforme', ['fichier' => $url]);
} else {
$erreur = _T('svp:message_nok_aucun_paquet_ajoute', ['url' => $url]);
}
return false;
}
// On met à jour le nombre de paquets et de plugins du depot maintenant !
sql_updateq(
'spip_depots',
['nbr_paquets' => $nb_paquets, 'nbr_plugins' => $nb_plugins, 'nbr_autres' => $nb_autres],
'id_depot=' . sql_quote($id_depot)
);
// On vide les paquets locaux pour mettre a jour leurs donnees relatives au depot
// comme les mises a jour disponibles
include_spip('inc/svp_depoter_local');
svp_actualiser_paquets_locaux(true);
return true;
}
/**
* Suppression d'un dépot et de son contenu (paquets, plugins) dans la base de données
*
* Cette suppression entraîne des recalcul comme les versions maximales
* des plugins téléchargeables qui peuvent changer.
*
* @uses svp_actualiser_url_plugins()
* @uses svp_nettoyer_apres_suppression()
* @uses svp_base_supprimer_paquets_locaux()
*
* @param int $id
* Identifiant du dépot
* @return bool
* false si le dépot n'est pas trouvé, true sinon
*/
function svp_supprimer_depot($id) {
$id = intval($id);
// Pas de depot a cet id ?
if (!$id_depot = sql_getfetsel('id_depot', 'spip_depots', 'id_depot=' . sql_quote($id))) {
return false;
}
// on calcule les versions max des plugins heberges par le depot
$vmax = [];
if ($resultats = sql_allfetsel('id_plugin, version', 'spip_paquets', 'id_depot=' . sql_quote($id))) {
foreach ($resultats as $paquet) {
$id_plugin = $paquet['id_plugin'];
if (
!isset($vmax[$id_plugin])
or (spip_version_compare($vmax[$id_plugin], $paquet['version'], '<'))
) {
$vmax[$id_plugin] = $paquet['version'];
}
}
}
// On supprime les paquets heberges par le depot
sql_delete('spip_paquets', 'id_depot=' . sql_quote($id_depot));
// Si on est pas en mode runtime, on utilise surement l'espace public pour afficher les plugins.
// Il faut donc verifier que les urls suivent bien la mise à jour
// Donc avant de nettoyer la base des plugins du depot ayant disparus on supprime toutes les urls
// associees a ce depot : on les recreera apres le nettoyage
if (!_SVP_MODE_RUNTIME) {
svp_actualiser_url_plugins();
}
// Nettoyer les autres relations à ce dépot
svp_nettoyer_apres_suppression($id_depot, $vmax);
// Si on est pas en mode runtime, on utilise surement l'espace public pour afficher les plugins.
// Il faut donc s'assurer que les urls suivent bien la mise à jour
// - on supprime toutes les urls plugin
// - on les regenere pour la liste des plugins mise a jour
if (!_SVP_MODE_RUNTIME) {
svp_actualiser_url_plugins();
}
// On supprime le depot lui-meme
sql_delete('spip_depots', 'id_depot=' . sql_quote($id_depot));
// on supprime les paquets locaux pour reactualisation
include_spip('inc/svp_depoter_local');
svp_base_supprimer_paquets_locaux();
return true;
}
/**
* Nettoyer la base de données après la suppression d'un dépot
*
* Supprime
* - les liens des plugins avec le dépot (table spip_depots_plugins)
* - les plugins dont aucun paquet n'est encore hébergé par un dépot restant (table spip_plugins)
* Remet à zéro la version maximale des plugins ayant vu leur paquet en version maximale supprimée
*
* @param int $id_depot
* Identifiant du dépot
* @param array $vmax
* Tableau de la version maximale des plugins du dépot supprimé
* Tableau (id_plugin => version maximale)
* @return bool
* true toujours.
**/
function svp_nettoyer_apres_suppression($id_depot, $vmax) {
// On rapatrie la liste des plugins du depot qui servira apres qu'on ait supprime les liens
// de la table spip_depots_plugins
$liens = sql_allfetsel('id_plugin', 'spip_depots_plugins', 'id_depot=' . sql_quote($id_depot));
$plugins_depot = array_column($liens, 'id_plugin');
// On peut donc supprimer tous ces liens *plugins-depots* du depot
sql_delete('spip_depots_plugins', 'id_depot=' . sql_quote($id_depot));
// On verifie pour chaque plugin concerne par la disparition de paquets si c'est la version
// la plus elevee qui a ete supprimee.
// Si oui, on positionne le vmax a 0, ce qui permettra de remettre a jour le plugin systematiquement
// a la prochaine actualisation.
// Cette operation est necessaire car on n'impose pas que les informations du plugin soient identiques
// pour chaque paquet !!!
// On insere, en encapsulant pour sqlite...
if (sql_preferer_transaction()) {
sql_demarrer_transaction();
}
if ($resultats = sql_allfetsel('id_plugin, vmax', 'spip_plugins', sql_in('id_plugin', $plugins_depot))) {
foreach ($resultats as $plugin) {
if (spip_version_compare($plugin['vmax'], $vmax[$plugin['id_plugin']], '=')) {
sql_updateq('spip_plugins', ['vmax' => ''], 'id_plugin=' . sql_quote($plugin['id_plugin']));
}
}
}
if (sql_preferer_transaction()) {
sql_terminer_transaction();
}
// Maintenant on calcule la liste des plugins du depot qui ne sont pas heberges
// par un autre depot => donc a supprimer
// - Liste de tous les plugins encore lies a un autre depot
// tous les plugins correspondants aux anciens paquets
$plugins_restants = sql_allfetsel('DISTINCT(id_plugin)', 'spip_paquets', sql_in('id_plugin', $plugins_depot));
$plugins_restants = array_column($plugins_restants, 'id_plugin');
// - L'intersection des deux tableaux renvoie les plugins a supprimer
$plugins_a_supprimer = array_diff($plugins_depot, $plugins_restants);
// On supprimer les plugins identifies
sql_delete('spip_plugins', sql_in('id_plugin', $plugins_a_supprimer));
return true;
}
/**
* Actualisation des plugins d'un dépot déjà crée
*
* Actualise les informations uniquement si la signature du fichier
* XML de description du dépot a changé
*
* @uses svp_actualiser_maj_version()
* @uses svp_actualiser_paquets()
* @uses svp_phraser_depot()
* @param int $id
* Identifiant du dépot
* @return bool
* false si erreur, true sinon
*/
function svp_actualiser_depot($id) {
include_spip('inc/distant');
$id = intval($id);
// pas de depot a cet id ?
if (!$depot = sql_fetsel('*', 'spip_depots', 'id_depot=' . sql_quote($id))) {
return false;
}
$fichier_xml = _DIR_RACINE . copie_locale($depot['xml_paquets'], 'modif');
$sha = sha1_file($fichier_xml);
if ($depot['sha_paquets'] == $sha) {
// Le fichier n'a pas change (meme sha1) alors on ne fait qu'actualiser la date
// de mise a jour du depot en mettant a jour *inutilement* le sha1
spip_log(
'Aucune modification du fichier XML, actualisation non declenchee - id_depot = ' . $depot['id_depot'],
'svp_actions.' . _LOG_INFO
);
sql_updateq('spip_depots', ['sha_paquets' => $sha], 'id_depot=' . $id);
} else {
// Le fichier a bien change il faut actualiser tout le depot
$infos = svp_phraser_depot($fichier_xml);
if (!$infos) {
return false;
}
// On actualise les paquets dans spip_paquets en premier lieu.
// Lors de la mise a jour des paquets, les plugins aussi sont actualises
$ok = svp_actualiser_paquets(
$depot['id_depot'],
$infos['paquets'],
$nb_paquets,
$nb_plugins,
$nb_autres
);
// apres la mise a jour des paquets d'un depot, on actualise les informations des paquets locaux
// principalement l'info "maj_version" indiquant s'il existe un paquet plus recent
include_spip('inc/svp_depoter_local');
svp_actualiser_maj_version();
if ($ok) {
// On met à jour :
// -- les infos ne pouvant pas etre editees par le formulaire d'edition
// d'un depot et extraites du xml
// -- le nombre de paquets et de plugins du depot ainsi que le nouveau sha1
// ce qui aura pour effet d'actualiser la date de mise a jour
$champs = [
'url_serveur' => $infos['depot']['url_serveur'],
'url_brouteur' => $infos['depot']['url_brouteur'],
'url_archives' => $infos['depot']['url_archives'],
'url_commits' => $infos['depot']['url_commits'],
'nbr_paquets' => $nb_paquets,
'nbr_plugins' => $nb_plugins,
'nbr_autres' => $nb_autres,
'sha_paquets' => $sha
];
sql_updateq('spip_depots', $champs, 'id_depot=' . sql_quote($depot['id_depot']));
}
}
return true;
}
/**
* Actualisation de la table des paquets pour le dépot choisi
*
* Enlève de la base les paquets du dépots qui ne sont plus présents
* dans la description du XML. Ajoute ou met à jour les autres.
*
* @uses svp_supprimer_plugins_orphelins()
* @uses svp_corriger_vmax_plugins()
* @uses svp_completer_plugins()
* @uses eclater_plugin_paquet()
* @uses svp_inserer_multi()
* @uses svp_completer_plugins_depot()
* @uses svp_actualiser_url_plugins()
*
* @param int $id_depot
* Identifiant du dépot
* @param array $paquets
* Tableau des paquets extraits du fichier XML
* L'index est le nom de l'archive (xxxx.zip) et le contenu est
* un tableau à deux entrées :
* - Index 'plugin' : le tableau des infos du plugin
* - Index 'file' : le nom de l'archive .zip
* @param int $nb_paquets
* Nombre de paquets réellement inserés dans la base
* @param int $nb_plugins
* Nombre de plugins parmi les paquets inserés
* @param int $nb_autres
* Nombre de contributions non issues de plugin parmi les paquets inserés
* @return bool
* false si aucun dépot ou paquets, true sinon
*/
function svp_actualiser_paquets($id_depot, $paquets, &$nb_paquets, &$nb_plugins, &$nb_autres) {
// Initialisation des compteurs
$nb_paquets = 0;
$nb_plugins = 0;
$nb_autres = 0;
// Si aucun depot ou aucun paquet on renvoie une erreur
if ((!$id_depot) or (!is_array($paquets))) {
return false;
}
// On initialise l'url de base des logos du depot et son type afin de
// calculer l'url complete de chaque logo
$select = ['url_archives', 'type'];
$depot = sql_fetsel($select, 'spip_depots', 'id_depot=' . sql_quote($id_depot));
// On supprime tous les paquets du depot
// qui ont ete evacues, c'est a dire ceux dont les signatures
// ne correspondent pas aux nouveaux...
// et on retablit les vmax des plugins restants...
$signatures = [];
foreach ($paquets as $_paquet) {
$signatures[] = $_paquet['md5'];
}
// tous les paquets du depot qui ne font pas parti des signatures
$anciens_paquets = sql_allfetsel(
'id_paquet',
'spip_paquets',
['id_depot=' . sql_quote($id_depot), sql_in('signature', $signatures, 'NOT')]
);
$anciens_paquets = array_column($anciens_paquets, 'id_paquet');
// pour ces vieux paquets, on les nettoie de la base
if ($anciens_paquets) {
// tous les plugins correspondants aux anciens paquets
$anciens_plugins = sql_allfetsel(
'pl.id_plugin',
['spip_plugins AS pl', 'spip_paquets AS pa'],
['pl.id_plugin=pa.id_plugin', sql_in('pa.id_paquet', $anciens_paquets)]
);
$anciens_plugins = array_column($anciens_plugins, 'id_plugin');
// suppression des anciens paquets
sql_delete('spip_paquets', sql_in('id_paquet', $anciens_paquets));
// suppressions des liaisons depots / anciens plugins
// on enlève la liaison lorsqu'il n'y a plus aucun paquet lie a un des plugins qui ont vu un paquet enlevé
// liste des plugins qui ont encore des paquets dans ce depot
$plugins_restants = sql_allfetsel(
'pl.id_plugin',
['spip_plugins AS pl', 'spip_paquets AS pa'],
[
sql_in('pl.id_plugin', $anciens_plugins),
'pl.id_plugin=pa.id_plugin',
'pa.id_depot=' . sql_quote($id_depot)
]
);
$plugins_restants = array_column($plugins_restants, 'id_plugin');
// par opposition, on retrouve ceux qui n'en ont plus...
$plugins_supprimes = array_diff($anciens_plugins, $plugins_restants);
sql_delete(
'spip_depots_plugins',
['id_depot=' . sql_quote($id_depot), sql_in('id_plugin', $plugins_supprimes)]
);
unset($plugins_restants, $plugins_supprimes);
// supprimer les plugins orphelins
include_spip('inc/svp_depoter_local');
svp_supprimer_plugins_orphelins($anciens_plugins);
// corriger les vmax des plugins
svp_corriger_vmax_plugins($anciens_plugins);
// corriger les compats, branches aussi
svp_completer_plugins($anciens_plugins);
}
// on ne garde que les paquets qui ne sont pas presents dans la base
$signatures = sql_allfetsel('signature', 'spip_paquets', 'id_depot=' . sql_quote($id_depot));
$signatures = array_column($signatures, 'signature');
foreach ($paquets as $cle => $_infos) {
if (in_array($_infos['md5'], $signatures)) {
unset($paquets[$cle]);
}
}
// tableaux d'actions
$insert_paquets = [];
$insert_plugins = [];
$insert_contribs = [];
$prefixes = []; // prefixe => id_plugin
// On met a jour ou on cree chaque paquet a partir du contenu du fichier xml
// On ne fait pas cas de la compatibilite avec la version de SPIP installee
// car l'operation doit permettre de collecter tous les paquets
foreach ($paquets as $_archive => $_infos) {
$insert_paquet = [];
// On initialise les informations specifiques au paquet :
// l'id du depot et les infos de l'archive
$insert_paquet['id_depot'] = $id_depot;
$insert_paquet['nom_archive'] = $_archive;
$insert_paquet['nbo_archive'] = $_infos['size'];
$insert_paquet['maj_archive'] = date('Y-m-d H:i:s', $_infos['date']);
$insert_paquet['src_archive'] = $_infos['source'];
$insert_paquet['date_modif'] = $_infos['last_commit'];
// On serialise le tableau des traductions par module
$insert_paquet['traductions'] = serialize($_infos['traductions']);
// On ajoute la signature
$insert_paquet['signature'] = $_infos['md5'];
// On verifie si le paquet est celui d'un plugin ou pas
// -- Les traitements du XML dependent de la DTD utilisee
// Formatage des informations extraites du plugin pour insertion dans la base SVP
$formater = charger_fonction('preparer_sql_' . $_infos['dtd'], 'plugins');
if ($champs_aplat = $formater($_infos['plugin'])) {
// Eclater les champs recuperes en deux sous tableaux, un par table (plugin, paquet)
$champs = eclater_plugin_paquet($champs_aplat);
$paquet_plugin = true;
// On complete les informations du paquet et du plugin
$insert_paquet = array_merge($insert_paquet, $champs['paquet']);
$insert_plugin = $champs['plugin'];
// Le logo est normalement fourni dans les infos de zip
if (!empty($_infos['logo'])) {
$insert_paquet['logo'] = $depot['url_archives'] . '/' . $_infos['logo'];
}
elseif ($insert_paquet['logo']) {
// Sinon on construit l'url complete du logo
// Le logo est maintenant disponible a la meme adresse que le zip et porte le nom du zip.
// Son extension originale est conservee
$insert_paquet['logo'] = $depot['url_archives'] . '/'
. preg_replace(',\.zip$,i', '', $insert_paquet['nom_archive']) . '.'
. pathinfo($insert_paquet['logo'], PATHINFO_EXTENSION);
}
} else {
$paquet_plugin = false;
}
// On teste l'existence du paquet dans la base avec les champs
// id_depot, nom_archive et src_archive pour être sur de l'unicité.
// - si le paquet n'existe pas, on le crée,
// - sinon (et ça ne devrait pas arriver), on ne fait qu'un update
if (
!$paquet = sql_fetsel('*', 'spip_paquets', [
'id_depot=' . sql_quote($insert_paquet['id_depot']),
'nom_archive=' . sql_quote($insert_paquet['nom_archive']),
'src_archive=' . sql_quote($insert_paquet['src_archive'])
])
) {
// Le paquet n'existe pas encore en base de donnees
// ------------------------------------------------
// On positionne la date de creation a celle du dernier commit ce qui est bien le cas
$insert_paquet['date_crea'] = $insert_paquet['date_modif'];
// Les collisions ne sont possibles que si on ajoute un nouveau paquet
$collision = false;
if ($paquet_plugin) {
// On est en presence d'un PLUGIN
// ------------------------------
// On evite les doublons de paquet
// Pour determiner un doublon on verifie actuellement :
// - le prefixe
// - la version du paquet et de la base
// - l'etat
$where = [
't1.id_plugin=t2.id_plugin',
't1.version=' . sql_quote($insert_paquet['version']),
't1.version_base=' . sql_quote($insert_paquet['version_base']),
't1.etatnum=' . sql_quote($insert_paquet['etatnum']),
't1.id_depot>' . intval(0),
't2.prefixe=' . sql_quote($insert_plugin['prefixe'])
];
if (!$id_paquet = sql_getfetsel('t1.id_paquet', 'spip_paquets AS t1, spip_plugins AS t2', $where)) {
// On traite d'abord le plugin du paquet pour recuperer l'id_plugin
// On rajoute le plugin dans la table spip_plugins si celui-ci n'y est pas encore ou on recupere
// l'id si il existe deja et on le met a jour si la version du paquet est plus elevee
$plugin = sql_fetsel(
'id_plugin, vmax',
'spip_plugins',
['prefixe=' . sql_quote($insert_plugin['prefixe'])]
);
if (!$plugin and !array_key_exists($insert_plugin['prefixe'], $insert_plugins)) {
$insert_plugins[$insert_plugin['prefixe']] = array_merge(
$insert_plugin,
['vmax' => $insert_paquet['version']]
);
} else {
if ($plugin) {
$id_plugin = $plugin['id_plugin'];
$prefixes[$insert_plugin['prefixe']] = $id_plugin;
}
if (
array_key_exists($insert_plugin['prefixe'], $insert_plugins)
and (spip_version_compare(
$insert_plugins[$insert_plugin['prefixe']]['vmax'],
$insert_paquet['version'],
'<='
))
) {
// attribuer au plugin le nom et le slogan du paquet le plus à jour
$insert_plugins[$insert_plugin['prefixe']]['nom'] = $insert_plugin['nom'];
$insert_plugins[$insert_plugin['prefixe']]['slogan'] = $insert_plugin['slogan'];
$insert_plugins[$insert_plugin['prefixe']]['vmax'] = $insert_paquet['version'];
}
}
// On traite maintenant le paquet connaissant l'id du plugin
// temporaire qui sera supprime lors de la connaissance de l'id_paquet
$insert_paquet['prefixe'] = $insert_plugin['prefixe'];
$insert_paquets[] = $insert_paquet;
} else {
$collision = true;
}
} else {
// On est en presence d'une CONTRIBUTION NON PLUGIN
// ------------------------------------------------
$where = [
'id_depot=' . sql_quote($insert_paquet['id_depot']),
'nom_archive=' . sql_quote($insert_paquet['nom_archive'])
];
if (!$id_paquet = sql_getfetsel('id_paquet', 'spip_paquets', $where)) {
// Ce n'est pas un plugin, donc id_plugin=0 et toutes les infos plugin sont nulles
$insert_paquet['id_plugin'] = 0;
$insert_contribs[] = $insert_paquet;
} else {
$collision = true;
}
}
// On loge le paquet ayant ete refuse dans un fichier a part afin de les verifier
// apres coup
if ($collision) {
spip_log(
'Collision avec le paquet <' . $insert_paquet['nom_archive'] .
' / ' . $insert_paquet['src_archive'] . '> du depot <' . $insert_paquet['id_depot'] . ">\n",
'svp_paquets.' . _LOG_INFO_IMPORTANTE
);
}
} else {
// Le paquet existe deja en base de donnees
// ----------------------------------------
// On ne devrait plus arriver ICI...
// Code obsolete ?
spip_log('!!!!!! Passage dans code obsolete (svp/svp_depoter_distant)', 'depoter');
// on effectue les traitements en attente
// pour que les updates soient corrects
svp_inserer_multi($insert_plugins, $insert_paquets, $insert_contribs, $prefixes);
// On met a jour le paquet en premier lieu qu'il soit un plugin ou une contribution
sql_updateq(
'spip_paquets',
$insert_paquet,
'id_paquet=' . sql_quote($paquet['id_paquet'])
);
}
}
// on effectue les traitements en attente
// pour que les updates soient corrects
svp_inserer_multi($insert_plugins, $insert_paquets, $insert_contribs, $prefixes);
// On rajoute le plugin comme heberge par le depot si celui-ci n'est pas encore enregistre comme tel
$ids = sql_allfetsel(
'p.id_plugin',
['spip_plugins AS p', 'spip_depots_plugins AS dp'],
['p.id_plugin=dp.id_plugin', 'dp.id_depot=' . sql_quote($id_depot)]
);
$ids = array_column($ids, 'id_plugin');
// inserer les liens avec le depots
$insert_dp = [];
$news_id = array_diff(array_values($prefixes), $ids);
foreach ($news_id as $id) {
$insert_dp[] = ['id_depot' => $id_depot, 'id_plugin' => $id];
}
if ($insert_dp) {
sql_insertq_multi('spip_depots_plugins', $insert_dp);
}
// on recalcul les vmax des plugins de ce depot.
svp_corriger_vmax_plugins(array_values($prefixes));
// On compile maintenant certaines informations des paquets mis a jour dans les plugins
// (date de creation, date de modif, version spip...)
svp_completer_plugins_depot($id_depot);
// Si on est pas en mode runtime, on utilise surement l'espace public pour afficher les plugins.
// Il faut donc s'assurer que les urls suivent bien la mise à jour
// - on supprime toutes les urls plugin
// - on les regenere pour la liste des plugins mise a jour
if (!_SVP_MODE_RUNTIME) {
svp_actualiser_url_plugins();
}
// Calcul des compteurs de paquets, plugins et contributions
$nb_paquets = sql_countsel('spip_paquets', 'id_depot=' . sql_quote($id_depot));
$nb_plugins = sql_countsel('spip_depots_plugins', 'id_depot=' . sql_quote($id_depot));
$nb_autres = sql_countsel('spip_paquets', ['id_depot=' . sql_quote($id_depot), 'id_plugin=0']);
return true;
}
/**
* Insertion en masse de plugins ou de paquets.
*
* Les paquets peuvent de pas avoir d'info "prefixe" (à transformer en id_plugin)
* lorsqu'ils ne proviennent pas de plugin (squelettes...)
*
* @param array $insert_plugins
* Tableau de description de plugins.
* Une description est un tableau de couples (colonne sql => valeur)
* pour l'insertion en base de données.
* @param array $insert_paquets
* Tableau de description de paquets.
* Une description est un tableau de couples (colonne sql => valeur)
* pour l'insertion en base de données.
* @param array $insert_contribs
* Tableau de description de paquets (contributions non plugins).
* Une description est un tableau de couples (colonne sql => valeur)
* pour l'insertion en base de données.
* @param array $prefixes
* Couples de relation (préfixe de plugin => identifiant de plugin) connues,
* pour limiter les accès SQL.
* @return void
**/
function svp_inserer_multi(&$insert_plugins, &$insert_paquets, &$insert_contribs, &$prefixes) {
if (count($insert_plugins)) {
sql_insertq_multi('spip_plugins', $insert_plugins);
$insert_plugins = [];
}
if (count($insert_paquets)) {
// on cherche tous les id_plugin/prefixe que l'on a à récuperer
// en une seule requete
$prefixes_manquants = [];
foreach ($insert_paquets as $p) {
// on ne connait que le prefixe
if (isset($p['prefixe']) and !isset($prefixes[$p['prefixe']])) {
$prefixes_manquants[] = $p['prefixe'];
}
}
// recuperer les nouveaux prefixes :
$new = sql_allfetsel(['prefixe', 'id_plugin'], 'spip_plugins', sql_in('prefixe', $prefixes_manquants));
foreach ($new as $p) {
$prefixes[$p['prefixe']] = $p['id_plugin'];
}
// inserer les id_plugin dans les paquets a inserer
// inserer le prefixe dans le paquet (pour raccourcis de jointures)
foreach ($insert_paquets as $c => $p) {
if (isset($p['prefixe'])) {
$insert_paquets[$c]['id_plugin'] = $prefixes[$insert_paquets[$c]['prefixe']];
} else {
$insert_paquets[$c]['prefixe'] = array_search($p['id_plugin'], $prefixes);
}
}
// on insere tout !
sql_insertq_multi('spip_paquets', $insert_paquets);
$insert_paquets = [];
}
// les contribs n'ont pas le même nombre de champs dans les insertions
// et n'ont pas de plugin rattachés.
if (count($insert_contribs)) {
sql_insertq_multi('spip_paquets', $insert_contribs);
$insert_contribs = [];
}
}
/**
* Complète les informations des plugins contenus dans un depot
* en compilant certaines informations (compatibilités, dates, branches)
*
* @uses svp_completer_plugins()
* @param int $id_depot
* Identifiant du depot à actualiser
**/
function svp_completer_plugins_depot($id_depot) {
// On limite la revue des paquets a ceux des plugins heberges par le depot en cours d'actualisation
$ids_plugins = sql_allfetsel('id_plugin', 'spip_depots_plugins', ['id_depot=' . sql_quote($id_depot)]);
$ids_plugins = array_column($ids_plugins, 'id_plugin');
if ($ids_plugins) {
svp_completer_plugins($ids_plugins);
}
}
/**
* Complète les informations des plugins, d'une liste de plugins donnés,
* en compilant certaines informations (compatibilités, dates, branches)
*
* @uses compiler_branches_spip()
* @param array $ids_plugin
* Liste d'identifiants de plugins
* @return bool
* false si rien à faire, true sinon
**/
function svp_completer_plugins($ids_plugin) {
if (!$ids_plugin) {
return false;
}
include_spip('inc/svp_outiller');
// -- on recupere tous les paquets associes aux plugins indiques et on compile les infos
if (
$resultats = sql_allfetsel(
'id_plugin, compatibilite_spip, date_crea, date_modif',
'spip_paquets',
[sql_in('id_plugin', $ids_plugin), 'id_depot>' . intval(0)],
'',
'id_plugin'
)
) {
$plugin_en_cours = 0;
$inserts = $complements = [];
foreach ($resultats as $paquet) {
// On finalise le plugin en cours et on passe au suivant
if ($plugin_en_cours != $paquet['id_plugin']) {
// On met a jour le plugin en cours
if ($plugin_en_cours) {
// On finalise maintenant les branches de la compatibilite globale du plugin
$branches = explode(',', $complements['branches_spip']);
$complements['branches_spip'] = implode(',', array_unique($branches));
$inserts[$plugin_en_cours] = $complements;
}
// On passe au plugin suivant
$plugin_en_cours = $paquet['id_plugin'];
$complements = ['compatibilite_spip' => '', 'branches_spip' => '', 'date_crea' => 0, 'date_modif' => 0];
}
// On compile les compléments du plugin avec le paquet courant
if ($paquet['date_modif'] > $complements['date_modif']) {
$complements['date_modif'] = $paquet['date_modif'];
}
if (
($complements['date_crea'] === 0)
or ($paquet['date_crea'] < $complements['date_crea'])
) {
$complements['date_crea'] = $paquet['date_crea'];
}
if ($paquet['compatibilite_spip']) {
if (!$complements['compatibilite_spip']) {
$complements['compatibilite_spip'] = $paquet['compatibilite_spip'];
} else {
$complements['compatibilite_spip'] = fusionner_intervalles(
$paquet['compatibilite_spip'],
$complements['compatibilite_spip']
);
}
}
if ($branches_paquet = compiler_branches_spip($paquet['compatibilite_spip'])) {
$complements['branches_spip'] .= ($complements['branches_spip'] ? ',' : '') . $branches_paquet;
}
}
// On finalise le dernier paquet en cours
$branches = explode(',', $complements['branches_spip']);
$complements['branches_spip'] = implode(',', array_unique($branches));
$inserts[$plugin_en_cours] = $complements;
// On insere, en encapsulant pour sqlite...
if (sql_preferer_transaction()) {
sql_demarrer_transaction();
}
foreach ($inserts as $id_plugin => $complements) {
sql_updateq('spip_plugins', $complements, 'id_plugin=' . intval($id_plugin));
}
if (sql_preferer_transaction()) {
sql_terminer_transaction();
}
}
return true;
}
/**
* Recrée toutes les URLs propres de plugin
*
* Supprime toutes les urls de plugin de la table spip_urls puis les régénère.
*
* @return int
* Nombre d'URLs de plugin régénérées
**/
function svp_actualiser_url_plugins() {
$nb_plugins = 0;
// On supprime toutes les urls de plugin
sql_delete('spip_urls', ['type=\'plugin\'']);
// On recupere les ids des plugins et on regenere les urls
if ($ids_plugin = sql_allfetsel('id_plugin', 'spip_plugins')) {
$ids_plugin = array_column($ids_plugin, 'id_plugin');
$nb_plugins = count($ids_plugin);
foreach ($ids_plugin as $_id) {
generer_objet_url($_id, 'plugin', '', '', true);
}
}
return $nb_plugins;
}
/**
* Éclate une description de paquet issu du XML du dépot en deux parties,
* une pour le plugin, l'autre pour le paquet
*
* Sépare en deux une description de champs désignant un paquet, en extrayant :
* - la partie plugin, soit ce qui peut être propre à plusieurs paquets.
* On trouve dedans le prefixe, nom, slogan, catégorie, tags
* - la partie paquet, soit ce qui est propre à ce conteneur là. On trouve
* dedans entre autres la description, la version, la compatibilité
* à SPIP, les dépendances, etc...
*
* @param array $champs_aplat
* Couples (clé => valeur) d'un paquet issu de l'analyse XML du dépot
* @return array
* Tableau de 2 index :
* - Index 'plugin' : couples (clé=>valeur) relatives au plugin
* - Index 'paquet' : couples (clé=>valeur) spécifiques au paquet
**/
function eclater_plugin_paquet($champs_aplat) {
return [
'plugin' => [
'prefixe' => $champs_aplat['prefixe'],
'nom' => $champs_aplat['nom'],
'slogan' => $champs_aplat['slogan']
],
'paquet' => [
'logo' => $champs_aplat['logo'],
'description' => $champs_aplat['description'],
'auteur' => $champs_aplat['auteur'],
'credit' => $champs_aplat['credit'],
'version' => $champs_aplat['version'],
'version_base' => $champs_aplat['version_base'],
'compatibilite_spip' => $champs_aplat['compatibilite_spip'],
'branches_spip' => $champs_aplat['branches_spip'],
'etat' => $champs_aplat['etat'],
'etatnum' => $champs_aplat['etatnum'],
'licence' => $champs_aplat['licence'],
'copyright' => $champs_aplat['copyright'],
'lien_doc' => $champs_aplat['lien_doc'],
'lien_demo' => $champs_aplat['lien_demo'],
'lien_dev' => $champs_aplat['lien_dev'],
'dependances' => $champs_aplat['dependances'],
'procure' => $champs_aplat['procure']
]
];
}
/**
* Détermine la version max de chaque plugin, c'est à dire
* la version maxi d'un des paquets qui lui est lié.
*
* @param array $plugins Liste d'identifiant de plugins
**/
function svp_corriger_vmax_plugins($plugins) {
// tous les plugins encore lies a des depots (hors local)...
// la vmax est a retablir...
if ($plugins) {
$p = sql_allfetsel(
'DISTINCT(p.id_plugin)',
['spip_plugins AS p', 'spip_paquets AS pa'],
[sql_in('p.id_plugin', $plugins), 'p.id_plugin=pa.id_plugin', 'pa.id_depot>' . intval(0)]
);
$p = array_column($p, 'id_plugin');
// pour les autres, on la fixe correctement
// On insere, en encapsulant pour sqlite...
if (sql_preferer_transaction()) {
sql_demarrer_transaction();
}
foreach ($p as $id_plugin) {
$vmax = '';
if ($pa = sql_allfetsel('version', 'spip_paquets', ['id_plugin=' . $id_plugin, 'id_depot>' . intval(0)])) {
foreach ($pa as $v) {
if (spip_version_compare($v['version'], $vmax, '>')) {
$vmax = $v['version'];
}
}
}
sql_updateq('spip_plugins', ['vmax' => $vmax], 'id_plugin=' . intval($id_plugin));
}
if (sql_preferer_transaction()) {
sql_terminer_transaction();
}
}
}