/** * Calcule une liste des paquets en fonctions de critères de recherche * * Cette liste : * - est sans doublons, ie on ne garde que la version la plus récente * - correspond aux critères * - est compatible avec la version SPIP installée sur le site * - ne liste pas ceux étant déjà installés (ces paquets peuvent toutefois être affichés) * - est triée par nom ou score * * @uses liste_des_champs() * @uses recherche_en_base() * * @param string $phrase * Texte de la recherche * @param string $categorie * Type de catégorie de plugin (auteur, date...) * @param string $etat * État de plugin (stable, test...) * @param string|int $depot * Identifiant de dépot * @param string $version_spip * Version de SPIP dont le paquet doit être compatible * @param array $exclusions * Liste d'identifiants de plugin à ne pas intégrer dans la liste * @param bool $afficher_exclusions * Afficher aussi les paquets déjà installés (true) * ou ceux qui ne le sont pas (false) ? * @param bool $doublon * Afficher toutes les versions de paquet (true) * ou seulement la plus récente (false) ? * @param string $tri * Ordre du tri : nom | score * * @return array * Tableau classé par pertinence de résultat * - 'prefixe' => tableau de description du paquet (si pas de doublons demandé) * - n => tableau de descriptions du paquet (si doublons autorisés) **/ function svp_rechercher_plugins_spip($phrase, $categorie, $etat, $depot, $version_spip = '', $exclusions = array(), $afficher_exclusions = false, $doublon = false, $tri = 'nom') { include_spip('inc/rechercher'); $plugins = array(); $scores = array(); $ids_paquets = array(); // On prepare l'utilisation de la recherche en base SPIP en la limitant aux tables spip_plugins // et spip_paquets si elle n'est pas vide if ($phrase) { $liste = liste_des_champs(); $tables = array('plugin' => $liste['plugin']); $options = array('jointures' => true, 'score' => true); // On cherche dans tous les enregistrements de ces tables des correspondances les plugins qui // correspondent a la phrase recherchee // -- On obtient une liste d'id de plugins et d'id de paquets $resultats = array('plugin' => array(), 'paquet' => array()); $resultats = recherche_en_base($phrase, $tables, $options); // -- On prepare le tableau des scores avec les paquets trouves par la recherche if ($resultats) { // -- On convertit les id de plugins en id de paquets $ids = array(); if (isset($resultats['plugin']) and $resultats['plugin']) { $ids_plugin = array_keys($resultats['plugin']); $where[] = sql_in('id_plugin', $ids_plugin); $ids = sql_allfetsel('id_paquet, id_plugin', 'spip_paquets', $where); } // -- On prepare les listes des id de paquet et des scores de ces memes paquets if (isset($resultats['paquet']) and $resultats['paquet']) { $ids_paquets = array_keys($resultats['paquet']); foreach ($resultats['paquet'] as $_id => $_score) { $scores[$_id] = intval($resultats['paquet'][$_id]['score']); } } // -- On merge les deux tableaux de paquets sans doublon en mettant a jour un tableau des scores foreach ($ids as $_ids) { $id_paquet = intval($_ids['id_paquet']); $id_plugin = intval($_ids['id_plugin']); if (array_search($id_paquet, $ids_paquets) === false) { $ids_paquets[] = $id_paquet; $scores[$id_paquet] = intval($resultats['plugin'][$id_plugin]['score']); } else { $scores[$id_paquet] = intval($resultats['paquet'][$id_paquet]['score']) + intval($resultats['plugin'][$id_plugin]['score']); } } } } else { if ($ids_paquets = sql_allfetsel('id_paquet', 'spip_paquets')) { $ids_paquets = array_map('reset', $ids_paquets); foreach ($ids_paquets as $_id) { $scores[$_id] = 0; } } } // Maintenant, on continue la recherche en appliquant, sur la liste des id de paquets, // les filtres complementaires : categorie, etat, exclusions et compatibilite spip // si on a bien trouve des resultats precedemment ou si aucune phrase n'a ete saisie // -- Preparation de la requete if ($ids_paquets) { $from = array('spip_plugins AS t1', 'spip_paquets AS t2', 'spip_depots AS t3'); $select = array('t1.nom AS nom', 't1.slogan AS slogan', 't1.prefixe AS prefixe', 't1.id_plugin AS id_plugin', 't2.id_paquet AS id_paquet', 't2.description AS description', 't2.compatibilite_spip AS compatibilite_spip', 't2.lien_doc AS lien_doc', 't2.auteur AS auteur', 't2.licence AS licence', 't2.etat AS etat', 't2.logo AS logo', 't2.version AS version', 't2.nom_archive AS nom_archive', 't3.url_archives AS url_archives'); $where = array('t1.id_plugin=t2.id_plugin', 't2.id_depot=t3.id_depot'); if ($ids_paquets) { $where[] = sql_in('t2.id_paquet', $ids_paquets); } if ($categorie and $categorie != 'toute_categorie') { $where[] = 't1.categorie=' . sql_quote($categorie); } if ($etat and $etat != 'tout_etat') { $where[] = 't2.etat=' . sql_quote($etat); } if ($depot and $depot != 'tout_depot') { $where[] = 't2.id_depot=' . sql_quote($depot); } if ($exclusions and !$afficher_exclusions) { $where[] = sql_in('t2.id_plugin', $exclusions, 'NOT'); } if ($resultats = sql_select($select, $from, $where)) { while ($paquets = sql_fetch($resultats)) { $prefixe = $paquets['prefixe']; $version = $paquets['version']; $nom = extraire_multi($paquets['nom']); $slogan = extraire_multi($paquets['slogan']); $description = extraire_multi($paquets['description']); if (svp_verifier_compatibilite_spip($paquets['compatibilite_spip'], $version_spip)) { // Le paquet remplit tous les criteres, on peut le selectionner // -- on utilise uniquement la langue du site $paquets['nom'] = $nom; $paquets['slogan'] = $slogan; $paquets['description'] = $description; // -- on ajoute le score si on a bien saisi une phrase if ($phrase) { $paquets['score'] = $scores[intval($paquets['id_paquet'])]; } else { $paquets['score'] = 0; } // -- on construit l'url de l'archive $paquets['url_archive'] = $paquets['url_archives'] . '/' . $paquets['nom_archive']; // -- on gere les exclusions si elle doivent etre affichees if ($afficher_exclusions and in_array($paquets['id_plugin'], $exclusions)) { $paquets['installe'] = true; } else { $paquets['installe'] = false; } // -- On traite les doublons (meme plugin, versions differentes) if ($doublon) { $plugins[] = $paquets; } else { // ajout // - si pas encore trouve // - ou si sa version est inferieure (on garde que la derniere version) if (!isset($plugins[$prefixe]) or !$plugins[$prefixe] or $plugins[$prefixe] and spip_version_compare($plugins[$prefixe]['version'], $version, '<')) { $plugins[$prefixe] = $paquets; } } } } } // On trie le tableau par score décroissant ou nom croissant $fonction = 'svp_trier_par_' . $tri; if ($doublon) { usort($plugins, $fonction); } else { uasort($plugins, $fonction); } } return $plugins; }
/** * Pour une description de paquet donnée, vérifie sa validité. * * Teste la version de SPIP, les librairies nécessitées, ses dépendances * (et tente de les trouver et ajouter si elles ne sont pas là) * * Lorsqu'une dépendance est activée, on entre en récursion * dans cette fonction avec la description de la dépendance * * @param array $info * Description du paquet * @param int $prof * Profondeur de récursion * @return bool * false si erreur (dépendance non résolue, incompatibilité...), true sinon **/ public function verifier_dependances_plugin($info, $prof = 0) { $this->log("- [{$prof}] verifier dependances " . $info['p']); $id = $info['i']; $err = false; // variable receptionnant parfois des erreurs $cache = array(); // cache des actions realisees dans ce tour // 1 // tester la version de SPIP de notre paquet // si on ne valide pas, on retourne une erreur ! // mais normalement, on ne devrait vraiment pas pouvoir tomber sur ce cas if (!svp_verifier_compatibilite_spip($info['compatibilite_spip'])) { $this->invalider($info); $this->erreur($id, _T('svp:message_incompatibilite_spip', array('plugin' => $info['n']))); return false; } // 2 // ajouter les librairies necessaires a notre paquet if (is_array($info['dl']) and count($info['dl'])) { foreach ($info['dl'] as $l) { // $l = array('nom' => 'x', 'lien' => 'url') $lib = $l['nom']; $this->log("## Necessite la librairie : " . $lib); // on verifie sa presence OU le fait qu'on pourra la telecharger if ($lib and !$this->est_presente_lib($lib)) { // peut on ecrire ? if (!is_writable(_DIR_LIB)) { $this->invalider($info); $this->erreur($id, _T('svp:message_erreur_ecriture_lib', array('plugin' => $info['n'], 'lib_url' => $l['lien'], 'lib' => $lib))); $err = true; } else { $this->change(array('i' => md5(serialize($l)), 'p' => $lib, 'n' => $lib, 'v' => $l['lien']), 'getlib'); $this->log("- La librairie {$lib} sera a télécharger"); } } } if ($err) { return false; } } // 3 // Trouver les dependences aux necessites // et les activer au besoin if (is_array($info['dn']) and count($info['dn'])) { foreach ($info['dn'] as $n) { $p = $n['nom']; $v = $n['compatibilite']; if ($p == 'SPIP') { // c'est pas la que ça se fait ! // ca ne devrait plus apparaitre comme dependence a un plugin. } elseif (array_key_exists($p, $this->procure) and plugin_version_compatible($v, $this->procure[$p], 'spip')) { // rien a faire... $this->log("-- est procure par le core ({$p})"); } else { $this->log("-- verifier : {$p}"); // nous sommes face a une dependance de plugin // on regarde s'il est present et a la bonne version // sinon on le cherche et on l'ajoute if ($ninfo = $this->sera_actif($p) and !($err = $this->en_erreur($ninfo['i'])) and plugin_version_compatible($v, $ninfo['v'])) { // il est deja actif ou a activer, et tout est ok $this->log('-- dep OK pour ' . $info['p'] . ' : ' . $p); } else { // absent ou erreur ou pas compatible $etat = $err ? 'erreur' : ($ninfo ? 'conflit' : 'absent'); // conflit signifie qu'il existe le prefixe actif, mais pas a la version demandee $this->log("Dependance " . $p . " a resoudre ! ({$etat})"); switch ($etat) { // commencons par le plus simple : // en cas d'absence, on cherche ou est ce plugin ! case 'absent': // on choisit par defaut le meilleur etat de plugin. // de preference dans les plugins locaux, sinon en distant. if (!$this->sera_off($p) and $new = $this->chercher_plugin_compatible($p, $v) and $this->verifier_dependances_plugin($new, ++$prof)) { // si le plugin existe localement et possede maj_version, // c'est que c'est peut etre une mise a jour + activation a faire // si le plugin // nouveau est local => non // nouveau est distant => oui peut etre $cache[] = $new; $i = array(); if (!$new['local']) { $i = $this->infos_courtes(array('pl.prefixe=' . sql_quote($new['p']), 'pa.maj_version=' . sql_quote($new['v'])), true); } if ($i and isset($i['p'][$new['p']]) and count($i['p'][$new['p']])) { // c'est une mise a jour $vieux = $i['p'][$new['p']][0]; $this->change($vieux, 'upon'); $this->log("-- update+active : {$p}"); } else { // tout nouveau tout beau $this->change($new, $new['local'] ? 'on' : 'geton'); if ($new['local']) { $this->log("-- nouveau present : {$p}"); } else { $this->log("-- nouveau distant : {$p}"); } } $this->add($new); } else { $this->log("-- !erreur : {$p}"); // on ne trouve pas la dependance ! $this->invalider($info); $this->erreur($id, $v ? _T('svp:message_dependance_plugin_version', array('plugin' => $info['n'], 'dependance' => $p, 'version' => $v)) : _T('svp:message_dependance_plugin', array('plugin' => $info['n'], 'dependance' => $p))); } unset($new, $vieux); break; case 'erreur': break; // present, mais conflit de version // de deux choses l'une : // soit on trouve un paquet meilleur... // soit pas :) // present, mais conflit de version // de deux choses l'une : // soit on trouve un paquet meilleur... // soit pas :) case 'conflit': $this->log(" conflit -> demande {$v}, present : " . $ninfo['v']); if (!$this->sera_off($p) and $new = $this->chercher_plugin_compatible($p, $v) and $this->verifier_dependances_plugin($new, ++$prof)) { // on connait le nouveau... $cache[] = $new; $this->remove($ninfo); $this->add($new); $this->change($ninfo, 'up'); $this->log("-- update : {$p}"); } else { $this->log("-- !erreur : {$p}"); // on ne trouve pas la dependance ! $this->invalider($info); $this->erreur($id, $v ? _T('svp:message_dependance_plugin_version', array('plugin' => $info['n'], 'dependance' => $p, 'version' => $v)) : _T('svp:message_dependance_plugin', array('plugin' => $info['n'], 'dependance' => $p))); } break; } } } if ($this->sera_invalide($info['p'])) { break; } } unset($n, $v, $p, $ninfo, $present, $conflit, $erreur, $err); // si le plugin est devenu invalide... // on invalide toutes les actions qu'on vient de faire ! if ($this->sera_invalide($info['p'])) { $this->log("> Purge du cache"); foreach ($cache as $i) { $this->invalider($i); } return false; } } return true; }