[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / plugins-dist / svp / inc / svp_actionner.php
index cf4e7a6..07a0c14 100644 (file)
@@ -1,14 +1,16 @@
 <?php
 
 /**
- * Gestion de l'actionneur : il effectue les actions sur les plugins 
+ * Gestion de l'actionneur : il effectue les actions sur les plugins
  *
  * @plugin SVP pour SPIP
  * @license GPL
  * @package SPIP\SVP\Actionneur
  */
-if (!defined("_ECRIRE_INC_VERSION")) return;
+
+if (!defined("_ECRIRE_INC_VERSION")) {
+       return;
+}
 
 
 /**
@@ -16,28 +18,32 @@ if (!defined("_ECRIRE_INC_VERSION")) return;
  * dans un fichier cache et de les effectuer.
  *
  * @package SPIP\SVP\Actionner
-**/
+ **/
 class Actionneur {
 
        /**
         * Instance du décideur
-        * @var Decideur */
-       var $decideur;
+        *
+        * @var Decideur
+        */
+       public $decideur;
 
        /**
         * Loguer les différents éléments
-        * 
+        *
         * Sa valeur sera initialisée par la configuration 'mode_log_verbeux' de SVP
-        * 
-        * @var bool */
-       var $log = false;
+        *
+        * @var bool
+        */
+       public $log = false;
 
        /**
         * Liste des actions à faire
+        *
         * @var array
         *     Tableau identifiant du paquet => type d'action
         */
-       var $start = array();
+       public $start = array();
 
        /**
         * Actions en cours d'analyse
@@ -47,14 +53,14 @@ class Actionneur {
         *
         * Chaque sous-tableau est composé d'une description courte du paquet
         * auquel est ajouté dans l'index 'todo' le type d'action à faire.
-        * 
+        *
         * @var array
         *     Index 'off' : les paquets à désactiver (ordre inverse des dépendances)
         *     Index 'lib' : les librairies à installer
         *     Index 'on' : les paquets à activer (ordre des dépendances)
         *     Index 'neutre' : autres actions dont l'ordre a peu d'importance.
         */
-       var $middle = array(
+       public $middle = array(
                'off' => array(),
                'lib' => array(),
                'on' => array(),
@@ -65,53 +71,61 @@ class Actionneur {
 
        /**
         * Liste des actions à faire
-        * 
+        *
         * Liste de description courtes des paquets + index 'todo' indiquant l'action
-        * @var array */
-       var $end = array();
+        *
+        * @var array
+        */
+       public $end = array();
 
        /**
         * Liste des actions faites
         * Liste de description courtes des paquets + index 'todo' indiquant l'action
-        * @var array */
-       var $done = array(); // faites
+        *
+        * @var array
+        */
+       public $done = array(); // faites
 
        /**
         * Actions en cours
         * Description courte du paquet + index 'todo' indiquant l'action
-        * @var array */
-       var $work = array();
+        *
+        * @var array
+        */
+       public $work = array();
 
        /**
         * Liste des erreurs
-        * 
-        * @var array Liste des erreurs */
-       var $err = array();
+        *
+        * @var array Liste des erreurs
+        */
+       public $err = array();
 
        /**
         * Verrou.
         * Le verrou est posé au moment de passer à l'action.
+        *
         * @var array
         *     Index 'id_auteur' : Identifiant de l'auteur ayant déclenché des actions
         *     Indix 'time' : timestamp de l'heure de déclenchement de l'action */
-       var $lock = array('id_auteur'=>0, 'time'=>'');
+       public $lock = array('id_auteur' => 0, 'time' => '');
 
        /**
         * SVP (ce plugin) est-il à désactiver dans une des actions ?
         *
         * Dans ce cas, on tente de le désactiver après d'autres plugins à désactiver
         * sinon l'ensemble des actions suivantes échoueraient.
-        * 
+        *
         * @var bool
         *     false si SVP n'est pas à désactiver, true sinon */
-       var $svp_off = false;
+       public $svp_off = false;
 
        /**
         * Constructeur
         *
         * Détermine si les logs sont activés et instancie un décideur.
         */
-       function Actionneur(){
+       public function __construct() {
                include_spip('inc/config');
                $this->log = (lire_config('svp/mode_log_verbeux') == 'oui');
 
@@ -131,23 +145,23 @@ class Actionneur {
         *
         * @param mixed $quoi
         *     La chose à logguer (souvent un texte)
-       **/
-       function log($quoi) {
+        **/
+       public function log($quoi) {
                if ($this->log) {
-                       spip_log($quoi,'actionneur');
+                       spip_log($quoi, 'actionneur');
                }
        }
 
        /**
         * Ajoute une erreur
-        * 
+        *
         * Ajoute une erreur à la liste des erreurs présentées au moment
         * de traiter les actions.
         *
         * @param string $erreur
         *     Le texte de l'erreur
-       **/
-       function err($erreur) {
+        **/
+       public function err($erreur) {
                if ($erreur) {
                        $this->err[] = $erreur;
                }
@@ -156,7 +170,7 @@ class Actionneur {
        /**
         * Remet à zéro les tableaux d'actions
         */
-       function clear() {
+       public function clear() {
                $this->middle = array(
                        'off' => array(),
                        'lib' => array(),
@@ -169,14 +183,16 @@ class Actionneur {
        }
 
        /**
-        * Ajoute les actions à faire dans l'actionneur 
+        * Ajoute les actions à faire dans l'actionneur
         *
         * @param array $todo
         *     Tableau des actions à faire (identifiant de paquet => type d'action)
-       **/
-       function ajouter_actions($todo) {
-               foreach ($todo as $id => $action) {
-                       $this->start[$id] = $action;
+        **/
+       public function ajouter_actions($todo) {
+               if ($todo) {
+                       foreach ($todo as $id => $action) {
+                               $this->start[$id] = $action;
+                       }
                }
                $this->ordonner_actions();
        }
@@ -192,15 +208,15 @@ class Actionneur {
         * @param string $nom Nom de la librairie
         * @param string $source URL pour obtenir la librairie
         */
-       function add_lib($nom, $source) {
+       public function add_lib($nom, $source) {
                if (!$this->decideur->est_presente_lib($nom)) {
                        if (is_writable(_DIR_LIB)) {
                                $this->middle['lib'][$nom] = array(
-                                       'todo'=>'getlib',
-                                       'n'=>$nom,
-                                       'p'=>$nom,
-                                       'v'=>$source,
-                                       's'=>$source,
+                                       'todo' => 'getlib',
+                                       'n' => $nom,
+                                       'p' => $nom,
+                                       'v' => $source,
+                                       's' => $source,
                                );
                        } else {
                                // erreur : impossible d'ecrire dans _DIR_LIB !
@@ -208,6 +224,7 @@ class Actionneur {
                                return false;
                        }
                }
+
                return true;
        }
 
@@ -231,13 +248,31 @@ class Actionneur {
         * - puis ce qui est a installer (à commencer par les librairies, puis paquets),
         * - puis les actions neutres
         */
-       function ordonner_actions() {
+       public function ordonner_actions() {
+               $this->log("Ordonner les actions à réaliser");
                // nettoyer le terrain
                $this->clear();
 
-               foreach ($this->start as $id=>$action) {
-                       $i = $this->decideur->infos_courtes_id($id);
-                       $i = $i['i'][$id];
+               // récupérer les descriptions de chaque paquet
+               $this->log("> Récupérer les descriptions des paquets concernés");
+               $infos = array();
+               foreach ($this->start as $id => $action) {
+                       // seulement les identifiants de paquets (pas les librairies)
+                       if (is_int($id)) {
+                               $info = $this->decideur->infos_courtes_id($id);
+                               $infos[$id] = $info['i'][$id];
+                       }
+               }
+
+               // Calculer les dépendances (nécessite) profondes pour chaque paquet, 
+               // si les plugins en questions sont parmis ceux actionnés
+               // (ie A dépend de B qui dépend de C => a dépend de B et C).
+               $infos = $this->calculer_necessites_complets($infos);
+
+               foreach ($this->start as $id => $action) {
+                       // infos du paquet. Ne s'applique pas sur librairie ($id = md5)
+                       $i = is_int($id) ? $infos[$id] : array(); 
+
                        switch ($action) {
                                case 'getlib':
                                        // le plugin en ayant besoin le fera
@@ -249,7 +284,7 @@ class Actionneur {
                                        break;
                                case 'up':
                                        // si le plugin est actif
-                                       if ($i['a'] == 'oui') { 
+                                       if ($i['a'] == 'oui') {
                                                $this->on($i, $action);
                                        } else {
                                                $this->neutre($i, $action);
@@ -287,7 +322,7 @@ class Actionneur {
                                }
                        }
                }
-       
+
                $this->log("------------");
                #$this->log("Fin du tri :");
                #$this->log($this->end);
@@ -295,7 +330,110 @@ class Actionneur {
 
 
        /**
-        * Ajoute un paquet à activer 
+        * Complète les infos des paquets actionnés pour qu'ils contiennent
+        * en plus de leurs 'necessite' directs, tous les nécessite des
+        * plugins dont ils dépendent, si ceux ci sont aussi actionnés.
+        * 
+        * Ie: si A indique dépendre de B, et B de C, la clé 
+        * 'dp' (dépendances prefixes). indiquera les préfixes
+        * des plugins B et C 
+        * 
+        * On ne s'occupe pas des versions de compatibilité ici
+        *
+        * @param array $infos (identifiant => description courte du plugin)
+        * @return array $infos
+       **/
+       public function calculer_necessites_complets($infos) {
+               $this->log("> Calculer les dépendances nécessités sur paquets concernés");
+
+               // prefixe => array(prefixes)
+               $necessites = array();
+
+               // 1) déjà les préfixes directement nécessités
+               foreach ($infos as $i => $info) {
+                       if (!empty($info['dn'])) {
+                               # à remplacer par array_column($info['dn'], 'nom') un jour
+                               $necessites[$info['p']] = array();
+                               foreach ($info['dn'] as $n) { 
+                                       $necessites[$info['p']][] = $n['nom'];
+                               }
+                       }
+                       // préparer la clé dp (dépendances préfixes) et 'dmp' (dépendent de moi) vide
+                       $infos[$i]['dp'] = array();
+                       $infos[$i]['dmp'] = array();
+               }
+
+               if ($nb = count($necessites)) {
+                       $this->log(">- $nb plugins ont des nécessités");
+                       // 2) ensuite leurs dépendances, récursivement
+                       $necessites = $this->calculer_necessites_complets_rec($necessites);
+
+                       // 3) intégrer le résultat
+                       foreach ($infos as $i => $info) {
+                               if (!empty($necessites[$info['p']])) {
+                                       $infos[$i]['dp'] = $necessites[$info['p']];
+                               }
+                       }
+
+                       // 4) calculer une clé 'dmp' : liste des paquets actionnés qui dépendent de moi
+                       foreach ($infos as $i => $info) {
+                               $dmp = array();
+                               foreach ($necessites as $prefixe => $liste) {
+                                       if (in_array($info['p'], $liste)) {
+                                               $dmp[] = $prefixe;
+                                       }
+                               }
+                               $infos[$i]['dmp'] = $dmp;
+                       }
+               }
+
+               # $this->log($infos);
+
+               return $infos;
+       }
+
+       /**
+        * Fonction récursive pour calculer la liste de tous les préfixes
+        * de plugins nécessités par un autre. 
+        * 
+        * Avec une liste fermée connue d'avance des possibilités de plugins
+        * (ceux qui seront actionnés)
+        *
+        * @param array prefixe => liste de prefixe dont il dépend
+        * @param bool $profondeur
+        * @return array prefixe => liste de prefixe dont il dépend
+       **/
+       public function calculer_necessites_complets_rec($necessites, $profondeur = 0) {
+               $changement = false;
+               foreach ($necessites as $prefixe => $liste) {
+                       $n = count($liste);
+                       foreach ($liste as $prefixe_necessite) {
+                               // si un des plugins dépendants fait partie des plugins actionnés,
+                               // il faut aussi lui ajouter ses dépendances…
+                               if (isset($necessites[$prefixe_necessite])) {
+                                       $liste = array_unique(array_merge($liste, $necessites[$prefixe_necessite]));
+                               }
+                       }
+                       $necessites[$prefixe] = $liste;
+                       if ($n !== count($liste)) {
+                               $changement = true;
+                       }
+               }
+
+               // limiter à 10 les successions de dépendances !
+               if ($changement and $profondeur <= 10) {
+                       $necessites = $this->calculer_necessites_complets_rec($necessites, $profondeur++);
+               }
+
+               if ($changement and $profondeur > 10) {
+                       $this->log("! Problème de calcul de dépendances complètes : récursion probable. On stoppe.");
+               }
+
+               return $necessites;
+       }
+
+       /**
+        * Ajoute un paquet à activer
         *
         * À chaque fois qu'un nouveau paquet arrive ici, on le compare
         * avec ceux déjà présents pour savoir si on doit le traiter avant
@@ -308,26 +446,31 @@ class Actionneur {
         * librairies seront téléchargées avant l'activation des plugins,
         * le plugin aura donc sa librairie lorsqu'il sera activé)
         *
-        * 
+        *
         * @param array $info
         *     Description du paquet
         * @param string $action
         *     Action à réaliser (on, upon)
         * @return void
-       **/
-       function on($info, $action) {
+        **/
+       public function on($info, $action) {
                $info['todo'] = $action;
                $p = $info['p'];
                $this->log("ON: $p $action");
 
                // si dependance, il faut le mettre avant !
-               $in = $out = $deps = $deps_all = array();
+               $in = $out = $prov = $deps = $deps_all = array();
                // raz des cles pour avoir les memes que $out (utile reellement ?)
                $this->middle['on'] = array_values($this->middle['on']);
-               // ajout des dependance
-               foreach ($info['dn'] as $dep) {
-                       $in[]  = $dep['nom'];
+               // ajout des dependances
+               $in = $info['dp'];
+               // $info fourni ses procure
+               if (isset($info['procure']) and $info['procure']) {
+                       $prov = array_keys($info['procure']);
                }
+               // et se fournit lui meme evidemment
+               array_unshift($prov, $p);
+
                // ajout des librairies
                foreach ($info['dl'] as $lib) {
                        // il faudrait gerer un retour d'erreur eventuel !
@@ -339,12 +482,18 @@ class Actionneur {
                //
                // ainsi que les dependences de ces plugins (deps)
                // ie. ces plugins peuvent dependre de ce nouveau a activer.
-               foreach ($this->middle['on'] as $inf) {
-                       $out[] = $inf['p'];
-                       foreach ($inf['dn'] as $dep) {
-                               $deps[$inf['p']][] = $dep['nom'];
-                               $deps_all[] = $dep['nom'];
+               foreach ($this->middle['on'] as $k => $inf) {
+                       $out["$k:0"] = $inf['p'];
+                       if (isset($inf['procure']) and $inf['procure']) {
+                               $i = 1;
+                               foreach ($inf['procure'] as $procure => $v) {
+                                       $out["$k:$i"] = $inf['p'];
+                                       $i++;
+                               }
                        }
+
+                       $deps[$inf['p']] = $inf['dp'];
+                       $deps_all = array_merge($deps_all, $inf['dp']);
                }
 
 
@@ -360,21 +509,30 @@ class Actionneur {
                        // on place notre action juste apres la derniere dependance
                        if ($diff = array_intersect($in, $out)) {
                                $key = array();
-                               foreach($diff as $d) {$key[] = array_search($d, $out);}
+                               foreach ($diff as $d) {
+                                       $k = array_search($d, $out);
+                                       $k = explode(":", $k);
+                                       $key[] = reset($k);
+                               }
                                $key = max($key);
                                $this->log("- placer $p apres " . $this->middle['on'][$key]['p']);
                                if ($key == count($this->middle['on'])) {
                                        $this->middle['on'][] = $info;
                                } else {
-                                       array_splice($this->middle['on'], $key+1, 0, array($info));
+                                       array_splice($this->middle['on'], $key + 1, 0, array($info));
                                }
 
-                       // intersection = plugin dependant de celui-ci
-                       // on place notre plugin juste avant la premiere dependance a lui trouvee
-                       } elseif (in_array($p, $deps_all)) {
-                               foreach ($deps as $prefix=>$dep) {
-                                       if (in_array($p, $dep)) {
-                                               $key = array_search($prefix, $out);
+                               // intersection = plugin dependant de celui-ci
+                               // on place notre plugin juste avant la premiere dependance a lui trouvee
+                       } elseif (array_intersect($prov, $deps_all)) {
+                               foreach ($deps as $prefix => $dep) {
+                                       if ($diff = array_intersect($prov, $deps_all)) {
+                                               foreach ($diff as $d) {
+                                                       $k = array_search($d, $out);
+                                                       $k = explode(":", $k);
+                                                       $key[] = reset($k);
+                                               }
+                                               $key = min($key);
                                                $this->log("- placer $p avant $prefix qui en depend ($key)");
                                                if ($key == 0) {
                                                        array_unshift($this->middle['on'], $info);
@@ -385,9 +543,9 @@ class Actionneur {
                                        }
                                }
 
-                       // rien de particulier, il a des dependances mais les plugins
-                       // ne sont pas encore la ou les dependances sont deja actives
-                       // donc on le place tout en bas
+                               // rien de particulier, il a des dependances mais les plugins
+                               // ne sont pas encore la ou les dependances sont deja actives
+                               // donc on le place tout en bas
                        } else {
                                $this->log("- placer $p tout en bas");
                                $this->middle['on'][] = $info;
@@ -398,18 +556,18 @@ class Actionneur {
 
 
        /**
-        * Ajoute un paquet avec une action neutre 
+        * Ajoute un paquet avec une action neutre
         *
         * Ces actions seront traitées en dernier, et peu importe leur
         * ordre car elles n'entrent pas en conflit avec des dépendances.
-        * 
+        *
         * @param array $info
         *     Description du paquet
         * @param string $action
         *     Action à réaliser (kill, get, up (sur plugin inactif))
         * @return void
-       **/
-       function neutre($info, $action) {
+        **/
+       public function neutre($info, $action) {
                $info['todo'] = $action;
                $this->log("NEUTRE:  $info[p] $action");
                $this->middle['neutre'][] = $info;
@@ -426,14 +584,14 @@ class Actionneur {
         *
         * Si le paquet est une dépendance d'un autre plugin, il faut le mettre
         * après (pour désactiver avant celui qui en dépend).
-        * 
+        *
         * @param array $info
         *     Description du paquet
         * @param string $action
         *     Action à réaliser (kill, get, up (sur plugin inactif))
         * @return void
-       **/
-       function off($info, $action) {
+        **/
+       public function off($info, $action) {
                $info['todo'] = $action;
                $p = $info['p'];
                $this->log("OFF: $p $action");
@@ -442,45 +600,89 @@ class Actionneur {
                if ($p == 'SVP') {
                        $this->svp_off = true;
                }
-               
+
                // si dependance, il faut le mettre avant !
                $in = $out = array();
                // raz des cles pour avoir les memes que $out (utile reellement ?)
                $this->middle['off'] = array_values($this->middle['off']);
-               foreach ($info['dn'] as $dep) {
-                       $in[]  = $dep['nom'];
-               }
-               foreach ($this->middle['off'] as $inf)  {
+               // in : si un plugin en dépend, il faudra désactiver celui là avant.
+               $in = $info['dp'];
+
+               foreach ($this->middle['off'] as $inf) {
                        $out[] = $inf['p'];
                }
 
-               if (!$in) {
+               if (!$info['dn']) {
                        // ce plugin n'a pas de dependance, on le met en dernier !
                        $this->log("- placer $p tout en bas");
                        $this->middle['off'][] = $info;
                } else {
                        // ce plugin a des dependances,
                        // on le desactive juste avant elles.
-                       
+
                        // intersection = dependance presente aussi
                        // on place notre action juste avant la premiere dependance
                        if ($diff = array_intersect($in, $out)) {
                                $key = array();
-                               foreach($diff as $d) {$key[] = array_search($d, $out);}
+                               foreach ($diff as $d) {
+                                       $key[] = array_search($d, $out);
+                               }
                                $key = min($key);
                                $this->log("- placer $p avant " . $this->middle['off'][$key]['p']);
                                array_splice($this->middle['off'], $key, 0, array($info));
+                       // inversement des plugins dépendants de ce plugin sont présents…
+                       // on le met juste après le dernier
+                       } elseif ($diff = array_intersect($info['dmp'], $out)) {
+                               $key = array();
+                               foreach ($diff as $d) {
+                                       $key[] = array_search($d, $out);
+                               }
+                               $key = max($key);
+                               $this->log("- placer $p apres " . $this->middle['off'][$key]['p']);
+                               if ($key == count($this->middle['off'])) {
+                                       $this->middle['off'][] = $info;
+                               } else {
+                                       array_splice($this->middle['off'], $key + 1, 0, array($info));
+                               }
                        } else {
                                // aucune des dependances n'est a desactiver
                                // (du moins à ce tour ci),
                                // on le met en premier !
                                $this->log("- placer $p tout en haut");
-                               array_unshift($this->middle['off'], $info); // etait ->middle['on'] ?? ...
+                               array_unshift($this->middle['off'], $info); 
                        }
                }
                unset($diff, $in, $out);
        }
 
+       /**
+        * Retourne le texte qui présente la dernière action qui vient d'être réalisée
+        *
+        * @return string
+        **/
+       public function presenter_derniere_action() {
+               $i = end($this->done);
+               reset($this->done);
+               $texte = '';
+               if ($i) {
+                       $ok = ($i['done'] ? true : false);
+                       $oks = &$ok;
+                       $ok_texte = $ok ? 'ok' : 'fail';
+                       $cle_t = 'svp:message_action_finale_' . $i['todo'] . '_' . $ok_texte;
+                       $trads = array(
+                               'plugin' => $i['n'],
+                               'version' => denormaliser_version($i['v']),
+                       );
+                       if (isset($i['maj'])) {
+                               $trads['version_maj'] = denormaliser_version($i['maj']);
+                       }
+                       $texte = _T($cle_t, $trads);
+                       if (is_string($i['done'])) {
+                               $texte .= " <span class='$ok_texte'>$i[done]</span>";
+                       }
+               }
+               return $texte;
+       }
 
        /**
         * Retourne un bilan, texte HTML, des actions qui ont été faites
@@ -488,23 +690,23 @@ class Actionneur {
         * Si c'est un affichage du bilan de fin, et qu'il reste des actions
         * à faire, un lien est proposé pour faire supprimer ces actions restantes
         * et le verrou qui va avec.
-        * 
+        *
         * @param bool $fin
         *     Est-ce un affichage intermédiaire (false) ou le tout dernier (true).
         * @return string
         *     Bilan des actions au format HTML
-       **/
-       function presenter_actions($fin = false) {
+        **/
+       public function presenter_actions($fin = false) {
                $affiche = "";
 
                include_spip('inc/filtres_boites');
-               
+
                if (count($this->err)) {
                        $erreurs = "<ul>";
                        foreach ($this->err as $i) {
                                $erreurs .= "\t<li class='erreur'>" . $i . "</li>\n";
                        }
-                       $erreurs .= "</ul>"; 
+                       $erreurs .= "</ul>";
                        $affiche .= boite_ouvrir(_T('svp:actions_en_erreur'), 'error') . $erreurs . boite_fermer();
                }
 
@@ -516,14 +718,33 @@ class Actionneur {
                                $oks = &$ok;
                                $ok_texte = $ok ? 'ok' : 'fail';
                                $cle_t = 'svp:message_action_finale_' . $i['todo'] . '_' . $ok_texte;
-                               $texte = _T($cle_t, array(
+                               $trads = array(
                                        'plugin' => $i['n'],
                                        'version' => denormaliser_version($i['v']),
-                                       'version_maj' => denormaliser_version($i['maj'])));
+                               );
+                               if (isset($i['maj'])) {
+                                       $trads['version_maj'] = denormaliser_version($i['maj']);
+                               }
+                               $texte = _T($cle_t, $trads);
                                if (is_string($i['done'])) {
                                        $texte .= " <span class='$ok_texte'>$i[done]</span>";
                                }
-                               $done .= "\t<li class='$ok_texte'>$texte</li>\n";
+                               // si le plugin a ete active dans le meme lot, on remplace le message 'active' par le message 'installe'
+                               if ($i['todo'] == 'install' and $ok_texte == 'ok') {
+                                       $cle_t = 'svp:message_action_finale_' . 'on' . '_' . $ok_texte;
+                                       $texte_on = _T($cle_t, array(
+                                               'plugin' => $i['n'],
+                                               'version' => denormaliser_version($i['v']),
+                                               'version_maj' => denormaliser_version($i['maj'])
+                                       ));
+                                       if (strpos($done, $texte_on) !== false) {
+                                               $done = str_replace($texte_on, $texte, $done);
+                                               $texte = "";
+                                       }
+                               }
+                               if ($texte) {
+                                       $done .= "\t<li class='$ok_texte'>$texte</li>\n";
+                               }
                        }
                        $done .= "</ul>";
                        $affiche .= boite_ouvrir(_T('svp:actions_realises'), ($oks ? 'success' : 'notice')) . $done . boite_fermer();
@@ -532,10 +753,11 @@ class Actionneur {
                if (count($this->end)) {
                        $todo = "<ul>";
                        foreach ($this->end as $i) {
-                               $todo .= "\t<li>"._T('svp:message_action_'.$i['todo'],array(
-                                       'plugin'=>$i['n'],
-                                       'version'=>denormaliser_version($i['v']),
-                                       'version_maj'=>denormaliser_version($i['maj'])))."</li>\n";
+                               $todo .= "\t<li>" . _T('svp:message_action_' . $i['todo'], array(
+                                               'plugin' => $i['n'],
+                                               'version' => denormaliser_version($i['v']),
+                                               'version_maj' => denormaliser_version($i['maj'])
+                                       )) . "</li>\n";
                        }
                        $todo .= "</ul>\n";
                        $titre = ($fin ? _T('svp:actions_non_traitees') : _T('svp:actions_a_faire'));
@@ -554,10 +776,11 @@ class Actionneur {
                                $date = date('Y-m-d H:i:s', $time);
                                $todo .= "<br />\n";
                                $todo .= "<p class='error'>" . _T('svp:erreur_actions_non_traitees', array(
-                                       'auteur' => sql_getfetsel('nom', 'spip_auteurs', 'id_auteur=' . sql_quote($this->lock['id_auteur'])),
-                                       'date' => affdate_heure($date)
-                               )) . "</p>\n";
-                               $todo .= "<a href='" . parametre_url(self(), 'nettoyer_actions', '1'). "'>" . _T('svp:nettoyer_actions') . "</a>\n";
+                                               'auteur' => sql_getfetsel('nom', 'spip_auteurs', 'id_auteur=' . sql_quote($this->lock['id_auteur'])),
+                                               'date' => affdate_heure($date)
+                                       )) . "</p>\n";
+                               $todo .= "<a href='" . parametre_url(self(), 'nettoyer_actions',
+                                               '1') . "'>" . _T('svp:nettoyer_actions') . "</a>\n";
                        }
                        $affiche .= boite_ouvrir($titre, 'notice') . $todo . boite_fermer();
                }
@@ -566,27 +789,39 @@ class Actionneur {
                        include_spip('inc/filtres');
                        $affiche = wrap($affiche, "<div class='svp_retour'>");
                }
-               
+
                return $affiche;
        }
 
+       /**
+        * Retourne le pourcentage de progression des actions
+        * @return int|float
+        */
+       public function progression() {
+               if (count($this->done)) {
+                       return count($this->done) / (count($this->done) + count($this->end));
+               }
+               return 0;
+       }
+
        /**
         * Teste l'existance d'un verrou par un auteur ?
         *
         * Si un id_auteur est transmis, teste que c'est cet auteur
         * précis qui a posé le verrou.
         *
-        * @see Actionneur::verouiller()
-        * 
+        * @see Actionneur::verrouiller()
+        *
         * @param int|string $id_auteur
         *     Identifiant de l'auteur, ou vide
         * @return bool
         *     true si un verrou est là, false sinon
-       **/
-       function est_verrouille($id_auteur = '') {
+        **/
+       public function est_verrouille($id_auteur = '') {
                if ($id_auteur == '') {
                        return ($this->lock['id_auteur'] ? true : false);
                }
+
                return ($this->lock['id_auteur'] == $id_auteur);
        }
 
@@ -601,10 +836,10 @@ class Actionneur {
         * Le verrou est signé par l'id_auteur de l'auteur actuellement identifié.
         *
         * Le verrou sera sauvegardé en fichier avec la liste des actions
-        * 
+        *
         * @see Actionneur::sauver_actions()
-       **/
-       function verrouiller() {
+        **/
+       public function verrouiller() {
                $this->lock = array(
                        'id_auteur' => $GLOBALS['visiteur_session']['id_auteur'],
                        'time' => time(),
@@ -613,8 +848,8 @@ class Actionneur {
 
        /**
         * Enlève le verrou
-       **/
-       function deverrouiller() {
+        **/
+       public function deverrouiller() {
                $this->lock = array(
                        'id_auteur' => 0,
                        'time' => '',
@@ -629,15 +864,15 @@ class Actionneur {
         * les erreurs générées, et le verrouillage.
         *
         * Le cache peut être lu avec la méthode get_actions()
-        * 
+        *
         * @see Actionneur::get_actions()
-       **/
-       function sauver_actions() {
+        **/
+       public function sauver_actions() {
                $contenu = serialize(array(
                        'todo' => $this->end,
                        'done' => $this->done,
                        'work' => $this->work,
-                       'err'  => $this->err,
+                       'err' => $this->err,
                        'lock' => $this->lock,
                ));
                ecrire_fichier(_DIR_TMP . 'stp_actions.txt', $contenu);
@@ -648,16 +883,16 @@ class Actionneur {
         *
         * Restaure les informations contenues dans le fichier de cache
         * et écrites avec la méthode sauver_actions().
-        * 
+        *
         * @see Actionneur::sauver_actions()
-       **/
-       function get_actions() {
+        **/
+       public function get_actions() {
                lire_fichier(_DIR_TMP . 'stp_actions.txt', $contenu);
                $infos = unserialize($contenu);
-               $this->end  = $infos['todo'];
+               $this->end = $infos['todo'];
                $this->work = $infos['work'];
                $this->done = $infos['done'];
-               $this->err  = $infos['err'];
+               $this->err = $infos['err'];
                $this->lock = $infos['lock'];
        }
 
@@ -665,12 +900,12 @@ class Actionneur {
         * Nettoyage des actions et verrou
         *
         * Remet tout à zéro pour pouvoir repartir d'un bon pied.
-       **/
-       function nettoyer_actions() {
+        **/
+       public function nettoyer_actions() {
                $this->todo = array();
                $this->done = array();
                $this->work = array();
-               $this->err  = array();
+               $this->err = array();
                $this->deverrouiller();
                $this->sauver_actions();
        }
@@ -685,12 +920,12 @@ class Actionneur {
         * @return bool|array
         *     False si aucune action à faire,
         *     sinon tableau de description courte du paquet + index 'todo' indiquant l'action
-       **/
-       function one_action() {
+        **/
+       public function one_action() {
                // s'il reste des actions, on en prend une, et on la fait
                // de meme si une action est en cours mais pas terminee (timeout)
                // on tente de la refaire...
-               if (count($this->end) OR $this->work) {
+               if (count($this->end) or $this->work) {
                        // on verrouille avec l'auteur en cours pour
                        // que seul lui puisse effectuer des actions a ce moment la
                        if (!$this->est_verrouille()) {
@@ -718,6 +953,7 @@ class Actionneur {
                                $this->deverrouiller();
                                $this->sauver_actions();
                        }
+
                        return $action;
                } else {
                        // on ne devrait normalement plus tomber sur un cas de verrouillage ici
@@ -727,6 +963,7 @@ class Actionneur {
                                $this->sauver_actions();
                        }
                }
+
                return false;
        }
 
@@ -739,8 +976,8 @@ class Actionneur {
         * Place dans la clé 'done' de description courte du paquet
         * le résultat de l'action (un booléen indiquant si elle s'est bien
         * déroulée).
-       **/
-       function do_action() {
+        **/
+       public function do_action() {
                if ($do = $this->work) {
                        $todo = 'do_' . $do['todo'];
                        lire_metas(); // avoir les metas a jour
@@ -761,17 +998,19 @@ class Actionneur {
         * @return bool
         *     false si erreur, true sinon.
         */
-       function do_geton($info) {
+       public function do_geton($info) {
                if (!$this->tester_repertoire_plugins_auto()) {
                        return false;
                }
-               $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($info['i']));
+               $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
                if ($dirs = $this->get_paquet_id($i)) {
                        $this->activer_plugin_dossier($dirs['dossier'], $i);
+
                        return true;
                }
-               
-               $this->log("GetOn : Erreur de chargement du paquet " .$info['n']);
+
+               $this->log("GetOn : Erreur de chargement du paquet " . $info['n']);
+
                return false;
        }
 
@@ -785,21 +1024,22 @@ class Actionneur {
         * @return bool
         *     false si erreur, true sinon.
         */
-       function do_on($info) {
-               $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($info['i']));
+       public function do_on($info) {
+               $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
                // à télécharger ?
-               if ($i['id_zone'] > 0) {
+               if (isset($i['id_zone']) and $i['id_zone'] > 0) {
                        return $this->do_geton($info);
                }
-               
+
                // a activer uniquement
                // il faudra prendre en compte les autres _DIR_xx
-               if (in_array($i['constante'], array('_DIR_PLUGINS','_DIR_PLUGINS_SUPPL'))) {
+               if (in_array($i['constante'], array('_DIR_PLUGINS', '_DIR_PLUGINS_SUPPL'))) {
                        $dossier = rtrim($i['src_archive'], '/');
                        $this->activer_plugin_dossier($dossier, $i, $i['constante']);
+
                        return true;
                }
-               
+
                return false;
        }
 
@@ -813,7 +1053,7 @@ class Actionneur {
         *     false si erreur,
         *     description courte du nouveau plugin sinon.
         */
-       function do_up($info) {
+       public function do_up($info) {
                // ecriture du nouveau
                // suppression de l'ancien (si dans auto, et pas au meme endroit)
                // OU suppression des anciens fichiers
@@ -823,21 +1063,29 @@ class Actionneur {
 
                // $i est le paquet a mettre à jour (donc present)
                // $maj est le paquet a telecharger qui est a jour (donc distant)
-               
-               $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($info['i']));
+
+               $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
 
                // on cherche la mise a jour...
                // c'est a dire le paquet source que l'on met a jour.
                if ($maj = sql_fetsel('pa.*',
                        array('spip_paquets AS pa', 'spip_plugins AS pl'),
                        array(
-                       'pl.prefixe='.sql_quote($info['p']),
-                       'pa.version='.sql_quote($info['maj']),
-                       'pa.id_plugin = pl.id_plugin',
-                       'pa.id_depot>'.sql_quote(0)),
-                       '', 'pa.etatnum DESC', '0,1')) {
+                               'pl.prefixe=' . sql_quote($info['p']),
+                               'pa.version=' . sql_quote($info['maj']),
+                               'pa.id_plugin = pl.id_plugin',
+                               'pa.id_depot>' . sql_quote(0)
+                       ),
+                       '', 'pa.etatnum DESC', '0,1')
+               ) {
+
+                       // si dans auto, on autorise à mettre à jour depuis auto pour les VCS
+                       $dir_actuel_dans_auto = '';
+                       if (substr($i['src_archive'], 0, 5) == 'auto/') {
+                               $dir_actuel_dans_auto = substr($i['src_archive'], 5);
+                       }
 
-                       if ($dirs = $this->get_paquet_id($maj)) {
+                       if ($dirs = $this->get_paquet_id($maj, $dir_actuel_dans_auto)) {
                                // Si le plugin a jour n'est pas dans le meme dossier que l'ancien...
                                // il faut :
                                // - activer le plugin sur son nouvel emplacement (uniquement si l'ancien est actif)...
@@ -851,16 +1099,18 @@ class Actionneur {
                                        // alors que le nouveau est auto/X/Y ...
                                        // il faut prendre en compte ce cas particulier et ne pas ecraser auto/X !
                                        if (substr($i['src_archive'], 0, 5) == 'auto/' and (false === strpos($dirs['dossier'], $i['src_archive']))) {
-                                               if (supprimer_repertoire( constant($i['constante']) . $i['src_archive']) ) {
+                                               if (supprimer_repertoire(constant($i['constante']) . $i['src_archive'])) {
                                                        sql_delete('spip_paquets', 'id_paquet=' . sql_quote($info['i']));
                                                }
                                        }
                                }
 
                                $this->ajouter_plugin_interessants_meta($dirs['dossier']);
+
                                return $dirs;
                        }
                }
+
                return false;
        }
 
@@ -873,12 +1123,14 @@ class Actionneur {
         * @return bool
         *     false si erreur, true sinon
         */
-       function do_upon($info) {
-               $i = sql_fetsel('*', 'spip_paquets', 'id_paquet='.sql_quote($info['i']));
+       public function do_upon($info) {
+               $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
                if ($dirs = $this->do_up($info)) {
                        $this->activer_plugin_dossier($dirs['dossier'], $i, $i['constante']);
+
                        return true;
                }
+
                return false;
        }
 
@@ -891,20 +1143,21 @@ class Actionneur {
         * @return bool
         *     false si erreur, true sinon
         */
-       function do_off($info) {
-               $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($info['i']));
+       public function do_off($info) {
+               $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
                // il faudra prendre en compte les autres _DIR_xx
-               if (in_array($i['constante'], array('_DIR_PLUGINS','_DIR_PLUGINS_SUPPL'))) {
+               if (in_array($i['constante'], array('_DIR_PLUGINS', '_DIR_PLUGINS_SUPPL'))) {
                        include_spip('inc/plugin');
                        $dossier = rtrim($i['src_archive'], '/');
-                       ecrire_plugin_actifs(array(rtrim($dossier,'/')), false, 'enleve');
-                       sql_updateq('spip_paquets', array('actif'=>'non', 'installe'=>'non'), 'id_paquet='.sql_quote($info['i']));
+                       ecrire_plugin_actifs(array(rtrim($dossier, '/')), false, 'enleve');
+                       sql_updateq('spip_paquets', array('actif' => 'non', 'installe' => 'non'), 'id_paquet=' . sql_quote($info['i']));
                        $this->actualiser_plugin_interessants();
                        // ce retour est un rien faux...
                        // il faudrait que la fonction ecrire_plugin_actifs()
                        // retourne au moins d'eventuels message d'erreur !
                        return true;
                }
+
                return false;
        }
 
@@ -917,23 +1170,24 @@ class Actionneur {
         * @return bool
         *     false si erreur, true sinon
         */
-       function do_stop($info) {
-               $i = sql_fetsel('*','spip_paquets','id_paquet=' . sql_quote($info['i']));
+       public function do_stop($info) {
+               $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
                // il faudra prendre en compte les autres _DIR_xx
-               if (in_array($i['constante'], array('_DIR_PLUGINS','_DIR_PLUGINS_SUPPL'))) {
+               if (in_array($i['constante'], array('_DIR_PLUGINS', '_DIR_PLUGINS_SUPPL'))) {
                        include_spip('inc/plugin');
-                       $dossier = rtrim($i['src_archive'],'/');
+                       $dossier = rtrim($i['src_archive'], '/');
 
                        $installer_plugins = charger_fonction('installer', 'plugins');
                        // retourne :
                        // - false : pas de procedure d'install/desinstalle
                        // - true : operation deja faite
                        // - tableau : operation faite ce tour ci.
-                       $infos = $installer_plugins($dossier, 'uninstall');
-                       if (is_bool($infos) OR !$infos['install_test'][0]) {
+                       $infos = $installer_plugins($dossier, 'uninstall', $i['constante']);
+                       if (is_bool($infos) or !$infos['install_test'][0]) {
                                include_spip('inc/plugin');
                                ecrire_plugin_actifs(array($dossier), false, 'enleve');
-                               sql_updateq('spip_paquets', array('actif'=>'non', 'installe'=>'non'), 'id_paquet='.sql_quote($info['i']));
+                               sql_updateq('spip_paquets', array('actif' => 'non', 'installe' => 'non'), 'id_paquet=' . sql_quote($info['i']));
+
                                return true;
                        } else {
                                // echec
@@ -941,6 +1195,7 @@ class Actionneur {
                        }
                }
                $this->actualiser_plugin_interessants();
+
                return false;
        }
 
@@ -953,15 +1208,16 @@ class Actionneur {
         * @return bool
         *     false si erreur, true sinon
         */
-       function do_kill($info) {
+       public function do_kill($info) {
                // on reverifie que c'est bien un plugin auto !
                // il faudrait aussi faire tres attention sur un site mutualise
                // cette option est encore plus delicate que les autres...
-               $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($info['i']));
+               $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
+
+               if (in_array($i['constante'], array('_DIR_PLUGINS', '_DIR_PLUGINS_SUPPL'))
+                       and substr($i['src_archive'], 0, 5) == 'auto/'
+               ) {
 
-               if (in_array($i['constante'], array('_DIR_PLUGINS','_DIR_PLUGINS_SUPPL'))
-               and substr($i['src_archive'], 0, 5) == 'auto/') {
-                       
                        $dir = constant($i['constante']) . $i['src_archive'];
                        if (supprimer_repertoire($dir)) {
                                $id_plugin = sql_getfetsel('id_plugin', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
@@ -1010,6 +1266,7 @@ class Actionneur {
                                        }
                                        array_pop($chemins);
                                }
+
                                return true;
                        }
                }
@@ -1026,24 +1283,27 @@ class Actionneur {
         * @return bool
         *     false si erreur, true sinon
         */
-       function do_getlib($info) {
+       public function do_getlib($info) {
                if (!defined('_DIR_LIB') or !_DIR_LIB) {
                        $this->err(_T('svp:erreur_dir_dib_indefini'));
                        $this->log("/!\ Pas de _DIR_LIB defini !");
+
                        return false;
                }
                if (!is_writable(_DIR_LIB)) {
-                       $this->err(_T('svp:erreur_dir_dib_ecriture', array('dir' => _DIR_LIB )));
+                       $this->err(_T('svp:erreur_dir_dib_ecriture', array('dir' => _DIR_LIB)));
                        $this->log("/!\ Ne peut pas écrire dans _DIR_LIB !");
+
                        return false;
                }
-               if(!autoriser('plugins_ajouter')){
+               if (!autoriser('plugins_ajouter')) {
                        $this->err(_T('svp:erreur_auth_plugins_ajouter_lib'));
                        $this->log("/!\ Pas autorisé à ajouter des libs !");
+
                        return false;
                }
 
-               $this->log("Recuperer la librairie : " . $info['n'] );
+               $this->log("Recuperer la librairie : " . $info['n']);
 
                // on recupere la mise a jour...
                include_spip('action/teleporter');
@@ -1052,9 +1312,10 @@ class Actionneur {
                if ($ok === true) {
                        return true;
                }
-               
+
                $this->err($ok);
                $this->log("Téléporteur en erreur : " . $ok);
+
                return false;
        }
 
@@ -1067,15 +1328,16 @@ class Actionneur {
         * @return bool
         *     false si erreur, true sinon
         */
-       function do_get($info) {
+       public function do_get($info) {
                if (!$this->tester_repertoire_plugins_auto()) {
                        return false;
                }
 
                $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
-       
+
                if ($dirs = $this->get_paquet_id($info['i'])) {
                        $this->ajouter_plugin_interessants_meta($dirs['dossier']);
+
                        return true;
                }
 
@@ -1091,13 +1353,13 @@ class Actionneur {
         * @return bool
         *     false si erreur, true sinon
         */
-       function do_install($info) {
+       public function do_install($info) {
                return $this->installer_plugin($info);
        }
 
 
        /**
-        * Activer un plugin 
+        * Activer un plugin
         *
         * @param string $dossier
         *     Chemin du répertoire du plugin
@@ -1106,32 +1368,39 @@ class Actionneur {
         * @param string $constante
         *     Constante indiquant le chemin de base du plugin (_DIR_PLUGINS, _DIR_PLUGINS_SUPPL, _DIR_PLUGINS_DIST)
         * @return void
-       **/
-       function activer_plugin_dossier($dossier, $i, $constante='_DIR_PLUGINS') {
+        **/
+       public function activer_plugin_dossier($dossier, $i, $constante = '_DIR_PLUGINS') {
                include_spip('inc/plugin');
                $this->log("Demande d'activation de : " . $dossier);
-               
+
                //il faut absolument que tous les fichiers de cache
                // soient inclus avant modification, sinon un appel ulterieur risquerait
                // de charger des fichiers deja charges par un autre !
                // C'est surtout le ficher de fonction le probleme (options et pipelines
                // sont normalement deja charges).
-               if (@is_readable(_CACHE_PLUGINS_OPT)) {include_once(_CACHE_PLUGINS_OPT);}
-               if (@is_readable(_CACHE_PLUGINS_FCT)) {include_once(_CACHE_PLUGINS_FCT);}
-               if (@is_readable(_CACHE_PIPELINES))   {include_once(_CACHE_PIPELINES);}
+               if (@is_readable(_CACHE_PLUGINS_OPT)) {
+                       include_once(_CACHE_PLUGINS_OPT);
+               }
+               if (@is_readable(_CACHE_PLUGINS_FCT)) {
+                       include_once(_CACHE_PLUGINS_FCT);
+               }
+               if (@is_readable(_CACHE_PIPELINES)) {
+                       include_once(_CACHE_PIPELINES);
+               }
 
                include_spip('inc/plugin');
                ecrire_plugin_actifs(array($dossier), false, 'ajoute');
                $installe = $i['version_base'] ? 'oui' : 'non';
                if ($installe == 'oui') {
-                       if(!$i['constante'])
+                       if (!$i['constante']) {
                                $i['constante'] = '_DIR_PLUGINS';
+                       }
                        // installer le plugin au prochain tour
                        $new_action = array_merge($this->work, array(
-                               'todo'=>'install',
-                               'dossier'=>rtrim($dossier,'/'),
-                               'constante'=>$i['constante'],
-                               'v'=>$i['version'], // pas forcement la meme version qu'avant lors d'une mise a jour.
+                               'todo' => 'install',
+                               'dossier' => rtrim($dossier, '/'),
+                               'constante' => $i['constante'],
+                               'v' => $i['version'], // pas forcement la meme version qu'avant lors d'une mise a jour.
                        ));
                        array_unshift($this->end, $new_action);
                        $this->log("Demande d'installation de $dossier");
@@ -1149,10 +1418,10 @@ class Actionneur {
         * Décrémente chaque score de plugin présent dans la méta
         * 'plugins_interessants' et signifiant que ces plugins
         * ont été utilisés récemment.
-        * 
+        *
         * Les plugins atteignant un score de zéro sont évacués ce la liste.
         */
-       function actualiser_plugin_interessants() {
+       public function actualiser_plugin_interessants() {
                // Chaque fois que l'on valide des plugins,
                // on memorise la liste de ces plugins comme etant "interessants",
                // avec un score initial, qui sera decremente a chaque tour :
@@ -1162,33 +1431,33 @@ class Actionneur {
                if (!is_array($plugins_interessants)) {
                        $plugins_interessants = array();
                }
-               
+
                $dossiers = array();
                $dossiers_old = array();
-               foreach($plugins_interessants as $p => $score) {
+               foreach ($plugins_interessants as $p => $score) {
                        if (--$score > 0) {
                                $plugins_interessants[$p] = $score;
-                               $dossiers[$p.'/'] = true;
+                               $dossiers[$p . '/'] = true;
                        } else {
                                unset($plugins_interessants[$p]);
-                               $dossiers_old[$p.'/'] = true;
+                               $dossiers_old[$p . '/'] = true;
                        }
                }
 
                // enlever les anciens
                if ($dossiers_old) {
                        // ATTENTION, il faudra prendre en compte les _DIR_xx
-                       sql_updateq('spip_paquets', array('recent'=>0), sql_in('src_archive', array_keys($dossiers_old)));
+                       sql_updateq('spip_paquets', array('recent' => 0), sql_in('src_archive', array_keys($dossiers_old)));
                }
 
-               $plugs = sql_allfetsel('src_archive','spip_paquets', 'actif='.sql_quote('oui'));
+               $plugs = sql_allfetsel('src_archive', 'spip_paquets', 'actif=' . sql_quote('oui'));
                $plugs = array_map('array_shift', $plugs);
                foreach ($plugs as $dossier) {
                        $dossiers[$dossier] = true;
-                       $plugins_interessants[ rtrim($dossier, '/') ] = 30; // score initial
+                       $plugins_interessants[rtrim($dossier, '/')] = 30; // score initial
                }
 
-               $plugs = sql_updateq('spip_paquets', array('recent'=>1), sql_in('src_archive', array_keys($dossiers)));
+               $plugs = sql_updateq('spip_paquets', array('recent' => 1), sql_in('src_archive', array_keys($dossiers)));
                ecrire_meta('plugins_interessants', serialize($plugins_interessants));
        }
 
@@ -1202,7 +1471,7 @@ class Actionneur {
         * @param string $dir
         *     Chemin du répertoire du plugin
         */
-       function ajouter_plugin_interessants_meta($dir) {
+       public function ajouter_plugin_interessants_meta($dir) {
                $plugins_interessants = @unserialize($GLOBALS['meta']['plugins_interessants']);
                if (!is_array($plugins_interessants)) {
                        $plugins_interessants = array();
@@ -1219,25 +1488,25 @@ class Actionneur {
         * @return bool
         *     false si erreur, true sinon
         */
-       function installer_plugin($info){
+       public function installer_plugin($info) {
                // il faut info['dossier'] et info['constante'] pour installer
                if ($plug = $info['dossier']) {
                        $installer_plugins = charger_fonction('installer', 'plugins');
                        $infos = $installer_plugins($plug, 'install', $info['constante']);
                        if ($infos) {
                                // en absence d'erreur, on met a jour la liste des plugins installes...
-                               if (!is_array($infos) OR $infos['install_test'][0]) {
+                               if (!is_array($infos) or $infos['install_test'][0]) {
                                        $meta_plug_installes = @unserialize($GLOBALS['meta']['plugin_installes']);
                                        if (!$meta_plug_installes) {
-                                               $meta_plug_installes=array();
+                                               $meta_plug_installes = array();
                                        }
                                        $meta_plug_installes[] = $plug;
-                                       ecrire_meta('plugin_installes',serialize($meta_plug_installes),'non');
+                                       ecrire_meta('plugin_installes', serialize($meta_plug_installes), 'non');
                                }
 
                                if (!is_array($infos)) {
                                        // l'installation avait deja ete faite un autre jour
-                                       return true; 
+                                       return true;
                                } else {
                                        // l'installation est neuve
                                        list($ok, $trace) = $infos['install_test'];
@@ -1246,40 +1515,47 @@ class Actionneur {
                                        }
                                        // l'installation est en erreur
                                        $this->err(_T('svp:message_action_finale_install_fail',
-                                               array('plugin' => $info['n'], 'version'=>denormaliser_version($info['v']))) . "<br />" . $trace);
+                                                       array('plugin' => $info['n'], 'version' => denormaliser_version($info['v']))) . "<br />" . $trace);
                                }
                        }
                }
+
                return false;
        }
 
 
        /**
         * Télécharge un paquet
-        * 
+        *
         * Supprime les fichiers obsolètes (si présents)
         *
         * @param int|array $id_or_row
         *     Identifiant du paquet ou description ligne SQL du paquet
+        * @param string $dest_ancien
+        *     Chemin vers l'ancien répertoire (pour les mises à jour par VCS)
         * @return bool|array
         *     False si erreur.
         *     Tableau de 2 index sinon :
         *     - dir : Chemin du paquet téléchargé depuis la racine
         *     - dossier : Chemin du paquet téléchargé, depuis _DIR_PLUGINS
         */
-       function get_paquet_id($id_or_row) {
+       public function get_paquet_id($id_or_row, $dest_ancien = "") {
                // on peut passer direct le row sql...
                if (!is_array($id_or_row)) {
-                       $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($id_or_row));
+                       $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($id_or_row));
                } else {
                        $i = $id_or_row;
                }
                unset($id_or_row);
 
                if ($i['nom_archive'] and $i['id_depot']) {
-                       $this->log("Recuperer l'archive : " . $i['nom_archive'] );
-                       if ($adresse = sql_getfetsel('url_archives', 'spip_depots', 'id_depot='.sql_quote($i['id_depot']))) {
-                               $zip = $adresse . '/' . $i['nom_archive'];
+                       $this->log("Recuperer l'archive : " . $i['nom_archive']);
+                       // on récupère les informations intéressantes du dépot :
+                       // - url des archives
+                       // - éventuellement : type de serveur (svn, git) et url de la racine serveur (svn://..../)
+                       $adresses = sql_fetsel(array('url_archives', 'type', 'url_serveur'), 'spip_depots',
+                               'id_depot=' . sql_quote($i['id_depot']));
+                       if ($adresses and $adresse = $adresses['url_archives']) {
 
                                // destination : auto/prefixe/version (sinon auto/nom_archive/version)
                                $prefixe = sql_getfetsel('pl.prefixe',
@@ -1287,51 +1563,48 @@ class Actionneur {
                                        array('pa.id_plugin = pl.id_plugin', 'pa.id_paquet=' . sql_quote($i['id_paquet'])));
 
                                // prefixe
-                               $base =  ($prefixe ? strtolower($prefixe) : substr($i['nom_archive'], 0, -4) ); // enlever .zip ...
+                               $base = ($prefixe ? strtolower($prefixe) : substr($i['nom_archive'], 0, -4)); // enlever .zip ...
 
                                // prefixe/version
-                               $dest = $base . '/v' . denormaliser_version($i['version']);
-                               
-                               // si on tombe sur un auto/X ayant des fichiers (et pas uniquement des dossiers)
-                               // ou un dossier qui ne commence pas par 'v'
-                               // c'est que auto/X n'était pas chargé avec SVP
-                               // ce qui peut arriver lorsqu'on migre de SPIP 2.1 à 3.0
-                               // dans ce cas, on supprime auto X pour mettre notre nouveau paquet.
-                               $ecraser_base = false;
-                               if (is_dir(_DIR_PLUGINS_AUTO . $base)) {
-                                       $base_files = scandir(_DIR_PLUGINS_AUTO . $base);
-                                       if (is_array($base_files)) {
-                                               $base_files = array_diff($base_files, array('.', '..'));
-                                               foreach ($base_files as $f) {
-                                                       if (($f[0] != '.' and $f[0] != 'v') // commence pas par v
-                                                       OR ($f[0] != '.' and !is_dir(_DIR_PLUGINS_AUTO . $base . '/' . $f))) { // commence par v mais pas repertoire
-                                                               $ecraser_base = true;
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                               }
-                               if ($ecraser_base) {
+                               $dest_future = $base . '/v' . denormaliser_version($i['version']);
+
+                               // Nettoyer les vieux formats dans auto/
+                               if ($this->tester_repertoire_destination_ancien_format(_DIR_PLUGINS_AUTO . $base)) {
                                        supprimer_repertoire(_DIR_PLUGINS_AUTO . $base);
                                }
 
+                               // l'url est différente en fonction du téléporteur
+                               $teleporteur = $this->choisir_teleporteur($adresses['type']);
+                               if ($teleporteur == 'http') {
+                                       $url = $adresse . '/' . $i['nom_archive'];
+                                       $dest = $dest_future;
+                               } else {
+                                       $url = $adresses['url_serveur'] . '/' . $i['src_archive'];
+                                       $dest = $dest_ancien ? $dest_ancien : $dest_future;
+                               }
 
                                // on recupere la mise a jour...
                                include_spip('action/teleporter');
                                $teleporter_composant = charger_fonction('teleporter_composant', 'action');
-                               $ok = $teleporter_composant('http', $zip, _DIR_PLUGINS_AUTO . $dest);
+                               $ok = $teleporter_composant($teleporteur, $url, _DIR_PLUGINS_AUTO . $dest);
                                if ($ok === true) {
+                                       // pour une mise à jour via VCS, il faut rebasculer sur le nouveau nom de repertoire
+                                       if ($dest != $dest_future) {
+                                               rename(_DIR_PLUGINS_AUTO . $dest, _DIR_PLUGINS_AUTO . $dest_future);
+                                       }
+
                                        return array(
-                                               'dir'=> _DIR_PLUGINS_AUTO . $dest,
+                                               'dir' => _DIR_PLUGINS_AUTO . $dest,
                                                'dossier' => 'auto/' . $dest, // c'est depuis _DIR_PLUGINS ... pas bien en dur...
                                        );
                                }
                                $this->err($ok);
                                $this->log("Téléporteur en erreur : " . $ok);
                        } else {
-                               $this->log("Aucune adresse pour le dépot " . $i['id_depot'] );
+                               $this->log("Aucune adresse pour le dépot " . $i['id_depot']);
                        }
                }
+
                return false;
        }
 
@@ -1342,46 +1615,115 @@ class Actionneur {
         *
         * @return bool
         *     True si on peut écrire dedans, false sinon
-       **/
-       function tester_repertoire_plugins_auto() {
+        **/
+       public function tester_repertoire_plugins_auto() {
                include_spip('inc/plugin'); // pour _DIR_PLUGINS_AUTO
                if (!defined('_DIR_PLUGINS_AUTO') or !_DIR_PLUGINS_AUTO) {
                        $this->err(_T('svp:erreur_dir_plugins_auto_indefini'));
                        $this->log("/!\ Pas de _DIR_PLUGINS_AUTO defini !");
+
                        return false;
                }
                if (!is_writable(_DIR_PLUGINS_AUTO)) {
-                       $this->err(_T('svp:erreur_dir_plugins_auto_ecriture', array('dir'=>_DIR_PLUGINS_AUTO)));
+                       $this->err(_T('svp:erreur_dir_plugins_auto_ecriture', array('dir' => _DIR_PLUGINS_AUTO)));
                        $this->log("/!\ Ne peut pas écrire dans _DIR_PLUGINS_AUTO !");
+
                        return false;
                }
+
                return true;
        }
 
 
+       /**
+        * Teste si un répertoire du plugin auto, contenant un plugin
+        * est dans un ancien format auto/prefixe/ (qui doit alors être supprimé)
+        * ou dans un format normal auto/prefixe/vx.y.z
+        *
+        * @example
+        *     $this->tester_repertoire_destination_ancien_format(_DIR_PLUGINS_AUTO . $base);
+        *
+        * @param string $dir_dans_auto
+        *      Chemin du répertoire à tester
+        * @return bool
+        *      true si le répertoire est dans un ancien format
+        */
+       public function tester_repertoire_destination_ancien_format($dir_dans_auto) {
+               // si on tombe sur un auto/X ayant des fichiers (et pas uniquement des dossiers)
+               // ou un dossier qui ne commence pas par 'v'
+               // c'est que auto/X n'était pas chargé avec SVP
+               // ce qui peut arriver lorsqu'on migre de SPIP 2.1 à 3.0
+               // dans ce cas, on supprime auto X pour mettre notre nouveau paquet.
+               if (is_dir($dir_dans_auto)) {
+                       $base_files = scandir($dir_dans_auto);
+                       if (is_array($base_files)) {
+                               $base_files = array_diff($base_files, array('.', '..'));
+                               foreach ($base_files as $f) {
+                                       if (($f[0] != '.' and $f[0] != 'v') // commence pas par v
+                                               or ($f[0] != '.' and !is_dir($dir_dans_auto . '/' . $f))
+                                       ) { // commence par v mais pas repertoire
+                                               return true;
+                                       }
+                               }
+                       }
+               }
+
+               return false;
+       }
+
+
+       /**
+        * Teste s'il est possible d'utiliser un téléporteur particulier,
+        * sinon retourne le nom du téléporteur par défaut
+        *
+        * @param string $teleporteur Téléporteur VCS à tester
+        * @param string $defaut Téléporteur par défaut
+        *
+        * @return string Nom du téléporteur à utiliser
+        **/
+       public function choisir_teleporteur($teleporteur, $defaut = 'http') {
+               // Utiliser un teleporteur vcs si possible si demandé
+               if (defined('SVP_PREFERER_TELECHARGEMENT_PAR_VCS') and SVP_PREFERER_TELECHARGEMENT_PAR_VCS) {
+                       if ($teleporteur) {
+                               include_spip('teleporter/' . $teleporteur);
+                               $tester_teleporteur = "teleporter_{$teleporteur}_tester";
+                               if (function_exists($tester_teleporteur)) {
+                                       if ($tester_teleporteur()) {
+                                               return $teleporteur;
+                                       }
+                               }
+                       }
+               }
+
+               return $defaut;
+       }
+
+
        /**
         * Teste si le plugin SVP (celui-ci donc) a
-        * été désinstallé / désactivé dans les actions réalisées 
+        * été désinstallé / désactivé dans les actions réalisées
         *
         * @note
         *     On ne peut tester sa désactivation que dans le hit où la désinstallation
         *     est réalisée, puisque après, s'il a été désactivé, au prochain hit
         *     on ne connaîtra plus ce fichier !
-        * 
+        *
         * @return bool
         *     true si SVP a été désactivé, false sinon
-       **/
-       function tester_si_svp_desactive() {
+        **/
+       public function tester_si_svp_desactive() {
                foreach ($this->done as $d) {
                        if ($d['p'] == 'SVP'
-                       AND $d['done'] == true
-                       AND in_array($d['todo'], array('off', 'stop'))) {
+                               and $d['done'] == true
+                               and in_array($d['todo'], array('off', 'stop'))
+                       ) {
                                return true;
                        }
                }
+
                return false;
        }
-       
+
 }
 
 
@@ -1395,16 +1737,15 @@ class Actionneur {
  * @param string $redirect
  *     URL de retour
  * @return void
-**/
-function svp_actionner_traiter_actions_demandees($actions, &$retour,$redirect=null) {
-               $actionneur = new Actionneur();
-               $actionneur->ajouter_actions($actions);
-               $actionneur->verrouiller();
-               $actionneur->sauver_actions();
-               
-               $redirect = $redirect ? $redirect : generer_url_ecrire('admin_plugin');
-               $retour['redirect'] = generer_url_action('actionner', 'redirect='.urlencode($redirect));
-               set_request('_todo', '');
-               $retour['message_ok'] = _T("svp:action_patienter");
+ **/
+function svp_actionner_traiter_actions_demandees($actions, &$retour, $redirect = null) {
+       $actionneur = new Actionneur();
+       $actionneur->ajouter_actions($actions);
+       $actionneur->verrouiller();
+       $actionneur->sauver_actions();
+
+       $redirect = $redirect ? $redirect : generer_url_ecrire('admin_plugin');
+       $retour['redirect'] = generer_url_action('actionner', 'redirect=' . urlencode($redirect));
+       set_request('_todo', '');
+       $retour['message_ok'] = _T("svp:action_patienter");
 }
-?>