4 * Gestion de l'actionneur : il effectue les actions sur les plugins
6 * @plugin SVP pour SPIP
8 * @package SPIP\SVP\Actionneur
11 if (!defined("_ECRIRE_INC_VERSION")) {
17 * L'actionneur calcule l'ordre des actions, permet de les stocker
18 * dans un fichier cache et de les effectuer.
20 * @package SPIP\SVP\Actionner
25 * Instance du décideur
32 * Loguer les différents éléments
34 * Sa valeur sera initialisée par la configuration 'mode_log_verbeux' de SVP
41 * Liste des actions à faire
44 * Tableau identifiant du paquet => type d'action
46 public $start = array();
49 * Actions en cours d'analyse
51 * Lorsqu'on ajoute les actions à faire, elles sont réordonnées
52 * et classées dans ces quatre sous-tableaux
54 * Chaque sous-tableau est composé d'une description courte du paquet
55 * auquel est ajouté dans l'index 'todo' le type d'action à faire.
58 * Index 'off' : les paquets à désactiver (ordre inverse des dépendances)
59 * Index 'lib' : les librairies à installer
60 * Index 'on' : les paquets à activer (ordre des dépendances)
61 * Index 'neutre' : autres actions dont l'ordre a peu d'importance.
63 public $middle = array(
70 // actions à la fin (apres analyse, et dans l'ordre)
73 * Liste des actions à faire
75 * Liste de description courtes des paquets + index 'todo' indiquant l'action
79 public $end = array();
82 * Liste des actions faites
83 * Liste de description courtes des paquets + index 'todo' indiquant l'action
87 public $done = array(); // faites
91 * Description courte du paquet + index 'todo' indiquant l'action
95 public $work = array();
100 * @var array Liste des erreurs
102 public $err = array();
106 * Le verrou est posé au moment de passer à l'action.
109 * Index 'id_auteur' : Identifiant de l'auteur ayant déclenché des actions
110 * Indix 'time' : timestamp de l'heure de déclenchement de l'action */
111 public $lock = array('id_auteur' => 0, 'time' => '');
114 * SVP (ce plugin) est-il à désactiver dans une des actions ?
116 * Dans ce cas, on tente de le désactiver après d'autres plugins à désactiver
117 * sinon l'ensemble des actions suivantes échoueraient.
120 * false si SVP n'est pas à désactiver, true sinon */
121 public $svp_off = false;
126 * Détermine si les logs sont activés et instancie un décideur.
128 public function __construct() {
129 include_spip('inc/config');
130 $this->log
= (lire_config('svp/mode_log_verbeux') == 'oui');
132 include_spip('inc/svp_decider');
133 $this->decideur
= new Decideur();
134 #$this->decideur->start();
136 // pour denormaliser_version()
137 include_spip('svp_fonctions');
144 * Ajoute un log si la propriété $log l'autorise;
147 * La chose à logguer (souvent un texte)
149 public function log($quoi) {
151 spip_log($quoi, 'actionneur');
158 * Ajoute une erreur à la liste des erreurs présentées au moment
159 * de traiter les actions.
161 * @param string $erreur
162 * Le texte de l'erreur
164 public function err($erreur) {
166 $this->err
[] = $erreur;
171 * Remet à zéro les tableaux d'actions
173 public function clear() {
174 $this->middle
= array(
180 $this->end
= array();
181 $this->done
= array();
182 $this->work
= array();
186 * Ajoute les actions à faire dans l'actionneur
189 * Tableau des actions à faire (identifiant de paquet => type d'action)
191 public function ajouter_actions($todo) {
193 foreach ($todo as $id => $action) {
194 $this->start
[$id] = $action;
197 $this->ordonner_actions();
202 * Ajoute une librairie à installer
204 * Ajoute l'action de télécharger une librairie, si la libraire
205 * n'est pas déjà présente et si le répertoire de librairie est
208 * @param string $nom Nom de la librairie
209 * @param string $source URL pour obtenir la librairie
211 public function add_lib($nom, $source) {
212 if (!$this->decideur
->est_presente_lib($nom)) {
213 if (is_writable(_DIR_LIB
)) {
214 $this->middle
['lib'][$nom] = array(
222 // erreur : impossible d'ecrire dans _DIR_LIB !
223 // TODO : message et retour d'erreur a gerer...
232 * Ordonne les actions demandées
234 * La fonction définie quelles sont les actions graduellement réalisables.
235 * Si un plugin A dépend de B qui dépend de C
236 * - pour tout ce qui est à installer : ordre des dependances (d'abord C, puis B, puis A)
237 * - pour tout ce qui est à désinstaller : ordre inverse des dependances. (d'abord A, puis B, puis C)
239 * On commence donc par séparer
240 * - ce qui est à désinstaller,
241 * - ce qui est à installer,
242 * - les actions neutres (get, up sur non actif, kill)
244 * Dans les traitements, on commencera par faire
245 * - ce qui est à désinstaller (il est possible que certains plugins
246 * nécessitent la désinstallation d'autres présents - tel que : 1 seul
247 * service d'envoi de mail)
248 * - puis ce qui est a installer (à commencer par les librairies, puis paquets),
249 * - puis les actions neutres
251 public function ordonner_actions() {
252 $this->log("Ordonner les actions à réaliser");
253 // nettoyer le terrain
256 // récupérer les descriptions de chaque paquet
257 $this->log("> Récupérer les descriptions des paquets concernés");
259 foreach ($this->start
as $id => $action) {
260 // seulement les identifiants de paquets (pas les librairies)
262 $info = $this->decideur
->infos_courtes_id($id);
263 $infos[$id] = $info['i'][$id];
267 // Calculer les dépendances (nécessite) profondes pour chaque paquet,
268 // si les plugins en questions sont parmis ceux actionnés
269 // (ie A dépend de B qui dépend de C => a dépend de B et C).
270 $infos = $this->calculer_necessites_complets($infos);
272 foreach ($this->start
as $id => $action) {
273 // infos du paquet. Ne s'applique pas sur librairie ($id = md5)
274 $i = is_int($id) ?
$infos[$id] : array();
278 // le plugin en ayant besoin le fera
283 $this->on($i, $action);
286 // si le plugin est actif
287 if ($i['a'] == 'oui') {
288 $this->on($i, $action);
290 $this->neutre($i, $action);
294 $this->on($i, $action);
298 $this->off($i, $action);
302 $this->neutre($i, $action);
307 // c'est termine, on passe tout dans la fin...
308 foreach ($this->middle
as $acts) {
309 $this->end
= array_merge($this->end
, $acts);
312 // si on a vu une desactivation de SVP
313 // on le met comme derniere action...
314 // sinon on ne pourrait pas faire les suivantes !
315 if ($this->svp_off
) {
316 $this->log("SVP a desactiver a la fin.");
317 foreach ($this->end
as $c => $info) {
318 if ($info['p'] == 'SVP') {
319 unset($this->end
[$c]);
320 $this->end
[] = $info;
326 $this->log("------------");
327 #$this->log("Fin du tri :");
328 #$this->log($this->end);
333 * Complète les infos des paquets actionnés pour qu'ils contiennent
334 * en plus de leurs 'necessite' directs, tous les nécessite des
335 * plugins dont ils dépendent, si ceux ci sont aussi actionnés.
337 * Ie: si A indique dépendre de B, et B de C, la clé
338 * 'dp' (dépendances prefixes). indiquera les préfixes
341 * On ne s'occupe pas des versions de compatibilité ici
343 * @param array $infos (identifiant => description courte du plugin)
344 * @return array $infos
346 public function calculer_necessites_complets($infos) {
347 $this->log("> Calculer les dépendances nécessités sur paquets concernés");
349 // prefixe => array(prefixes)
350 $necessites = array();
352 // 1) déjà les préfixes directement nécessités
353 foreach ($infos as $i => $info) {
354 if (!empty($info['dn'])) {
355 # à remplacer par array_column($info['dn'], 'nom') un jour
356 $necessites[$info['p']] = array();
357 foreach ($info['dn'] as $n) {
358 $necessites[$info['p']][] = $n['nom'];
361 // préparer la clé dp (dépendances préfixes) et 'dmp' (dépendent de moi) vide
362 $infos[$i]['dp'] = array();
363 $infos[$i]['dmp'] = array();
366 if ($nb = count($necessites)) {
367 $this->log(">- $nb plugins ont des nécessités");
368 // 2) ensuite leurs dépendances, récursivement
369 $necessites = $this->calculer_necessites_complets_rec($necessites);
371 // 3) intégrer le résultat
372 foreach ($infos as $i => $info) {
373 if (!empty($necessites[$info['p']])) {
374 $infos[$i]['dp'] = $necessites[$info['p']];
378 // 4) calculer une clé 'dmp' : liste des paquets actionnés qui dépendent de moi
379 foreach ($infos as $i => $info) {
381 foreach ($necessites as $prefixe => $liste) {
382 if (in_array($info['p'], $liste)) {
386 $infos[$i]['dmp'] = $dmp;
390 # $this->log($infos);
396 * Fonction récursive pour calculer la liste de tous les préfixes
397 * de plugins nécessités par un autre.
399 * Avec une liste fermée connue d'avance des possibilités de plugins
400 * (ceux qui seront actionnés)
402 * @param array prefixe => liste de prefixe dont il dépend
403 * @param bool $profondeur
404 * @return array prefixe => liste de prefixe dont il dépend
406 public function calculer_necessites_complets_rec($necessites, $profondeur = 0) {
408 foreach ($necessites as $prefixe => $liste) {
410 foreach ($liste as $prefixe_necessite) {
411 // si un des plugins dépendants fait partie des plugins actionnés,
412 // il faut aussi lui ajouter ses dépendances…
413 if (isset($necessites[$prefixe_necessite])) {
414 $liste = array_unique(array_merge($liste, $necessites[$prefixe_necessite]));
417 $necessites[$prefixe] = $liste;
418 if ($n !== count($liste)) {
423 // limiter à 10 les successions de dépendances !
424 if ($changement and $profondeur <= 10) {
425 $necessites = $this->calculer_necessites_complets_rec($necessites, $profondeur++
);
428 if ($changement and $profondeur > 10) {
429 $this->log("! Problème de calcul de dépendances complètes : récursion probable. On stoppe.");
436 * Ajoute un paquet à activer
438 * À chaque fois qu'un nouveau paquet arrive ici, on le compare
439 * avec ceux déjà présents pour savoir si on doit le traiter avant
440 * ou après un des paquets à activer déjà présent.
442 * Si le paquet est une dépendance d'un autre plugin, il faut le mettre
443 * avant (pour l'activer avant celui qui en dépend).
445 * Si le paquet demande une librairie, celle-ci est ajoutée (les
446 * librairies seront téléchargées avant l'activation des plugins,
447 * le plugin aura donc sa librairie lorsqu'il sera activé)
451 * Description du paquet
452 * @param string $action
453 * Action à réaliser (on, upon)
456 public function on($info, $action) {
457 $info['todo'] = $action;
459 $this->log("ON: $p $action");
461 // si dependance, il faut le mettre avant !
462 $in = $out = $prov = $deps = $deps_all = array();
463 // raz des cles pour avoir les memes que $out (utile reellement ?)
464 $this->middle
['on'] = array_values($this->middle
['on']);
465 // ajout des dependances
467 // $info fourni ses procure
468 if (isset($info['procure']) and $info['procure']) {
469 $prov = array_keys($info['procure']);
471 // et se fournit lui meme evidemment
472 array_unshift($prov, $p);
474 // ajout des librairies
475 foreach ($info['dl'] as $lib) {
476 // il faudrait gerer un retour d'erreur eventuel !
477 $this->add_lib($lib['nom'], $lib['lien']);
480 // on recupere : tous les prefix de plugin a activer (out)
481 // ie. ce plugin peut dependre d'un de ceux la
483 // ainsi que les dependences de ces plugins (deps)
484 // ie. ces plugins peuvent dependre de ce nouveau a activer.
485 foreach ($this->middle
['on'] as $k => $inf) {
486 $out["$k:0"] = $inf['p'];
487 if (isset($inf['procure']) and $inf['procure']) {
489 foreach ($inf['procure'] as $procure => $v) {
490 $out["$k:$i"] = $inf['p'];
495 $deps[$inf['p']] = $inf['dp'];
496 $deps_all = array_merge($deps_all, $inf['dp']);
502 // pas de dependance, on le met en premier !
503 $this->log("- placer $p tout en haut");
504 array_unshift($this->middle
['on'], $info);
508 // intersection = dependance presente aussi
509 // on place notre action juste apres la derniere dependance
510 if ($diff = array_intersect($in, $out)) {
512 foreach ($diff as $d) {
513 $k = array_search($d, $out);
514 $k = explode(":", $k);
518 $this->log("- placer $p apres " . $this->middle
['on'][$key]['p']);
519 if ($key == count($this->middle
['on'])) {
520 $this->middle
['on'][] = $info;
522 array_splice($this->middle
['on'], $key +
1, 0, array($info));
525 // intersection = plugin dependant de celui-ci
526 // on place notre plugin juste avant la premiere dependance a lui trouvee
527 } elseif (array_intersect($prov, $deps_all)) {
528 foreach ($deps as $prefix => $dep) {
529 if ($diff = array_intersect($prov, $deps_all)) {
530 foreach ($diff as $d) {
531 $k = array_search($d, $out);
532 $k = explode(":", $k);
536 $this->log("- placer $p avant $prefix qui en depend ($key)");
538 array_unshift($this->middle
['on'], $info);
540 array_splice($this->middle
['on'], $key, 0, array($info));
546 // rien de particulier, il a des dependances mais les plugins
547 // ne sont pas encore la ou les dependances sont deja actives
548 // donc on le place tout en bas
550 $this->log("- placer $p tout en bas");
551 $this->middle
['on'][] = $info;
554 unset($diff, $in, $out);
559 * Ajoute un paquet avec une action neutre
561 * Ces actions seront traitées en dernier, et peu importe leur
562 * ordre car elles n'entrent pas en conflit avec des dépendances.
565 * Description du paquet
566 * @param string $action
567 * Action à réaliser (kill, get, up (sur plugin inactif))
570 public function neutre($info, $action) {
571 $info['todo'] = $action;
572 $this->log("NEUTRE: $info[p] $action");
573 $this->middle
['neutre'][] = $info;
577 * Ajoute un paquet à désactiver
579 * Ces actions seront traitées en premier.
581 * À chaque fois qu'un nouveau paquet arrive ici, on le compare
582 * avec ceux déjà présents pour savoir si on doit le traiter avant
583 * ou après un des paquets à désactiver déjà présent.
585 * Si le paquet est une dépendance d'un autre plugin, il faut le mettre
586 * après (pour désactiver avant celui qui en dépend).
589 * Description du paquet
590 * @param string $action
591 * Action à réaliser (kill, get, up (sur plugin inactif))
594 public function off($info, $action) {
595 $info['todo'] = $action;
597 $this->log("OFF: $p $action");
599 // signaler la desactivation de SVP
601 $this->svp_off
= true;
604 // si dependance, il faut le mettre avant !
605 $in = $out = array();
606 // raz des cles pour avoir les memes que $out (utile reellement ?)
607 $this->middle
['off'] = array_values($this->middle
['off']);
608 // in : si un plugin en dépend, il faudra désactiver celui là avant.
611 foreach ($this->middle
['off'] as $inf) {
616 // ce plugin n'a pas de dependance, on le met en dernier !
617 $this->log("- placer $p tout en bas");
618 $this->middle
['off'][] = $info;
620 // ce plugin a des dependances,
621 // on le desactive juste avant elles.
623 // intersection = dependance presente aussi
624 // on place notre action juste avant la premiere dependance
625 if ($diff = array_intersect($in, $out)) {
627 foreach ($diff as $d) {
628 $key[] = array_search($d, $out);
631 $this->log("- placer $p avant " . $this->middle
['off'][$key]['p']);
632 array_splice($this->middle
['off'], $key, 0, array($info));
633 // inversement des plugins dépendants de ce plugin sont présents…
634 // on le met juste après le dernier
635 } elseif ($diff = array_intersect($info['dmp'], $out)) {
637 foreach ($diff as $d) {
638 $key[] = array_search($d, $out);
641 $this->log("- placer $p apres " . $this->middle
['off'][$key]['p']);
642 if ($key == count($this->middle
['off'])) {
643 $this->middle
['off'][] = $info;
645 array_splice($this->middle
['off'], $key +
1, 0, array($info));
648 // aucune des dependances n'est a desactiver
649 // (du moins à ce tour ci),
650 // on le met en premier !
651 $this->log("- placer $p tout en haut");
652 array_unshift($this->middle
['off'], $info);
655 unset($diff, $in, $out);
659 * Retourne le texte qui présente la dernière action qui vient d'être réalisée
663 public function presenter_derniere_action() {
664 $i = end($this->done
);
668 $ok = ($i['done'] ?
true : false);
670 $ok_texte = $ok ?
'ok' : 'fail';
671 $cle_t = 'svp:message_action_finale_' . $i['todo'] . '_' . $ok_texte;
674 'version' => denormaliser_version($i['v']),
676 if (isset($i['maj'])) {
677 $trads['version_maj'] = denormaliser_version($i['maj']);
679 $texte = _T($cle_t, $trads);
680 if (is_string($i['done'])) {
681 $texte .= " <span class='$ok_texte'>$i[done]</span>";
688 * Retourne un bilan, texte HTML, des actions qui ont été faites
690 * Si c'est un affichage du bilan de fin, et qu'il reste des actions
691 * à faire, un lien est proposé pour faire supprimer ces actions restantes
692 * et le verrou qui va avec.
695 * Est-ce un affichage intermédiaire (false) ou le tout dernier (true).
697 * Bilan des actions au format HTML
699 public function presenter_actions($fin = false) {
702 include_spip('inc/filtres_boites');
704 if (count($this->err
)) {
706 foreach ($this->err
as $i) {
707 $erreurs .= "\t<li class='erreur'>" . $i . "</li>\n";
710 $affiche .= boite_ouvrir(_T('svp:actions_en_erreur'), 'error') . $erreurs . boite_fermer();
713 if (count($this->done
)) {
716 foreach ($this->done
as $i) {
717 $ok = ($i['done'] ?
true : false);
719 $ok_texte = $ok ?
'ok' : 'fail';
720 $cle_t = 'svp:message_action_finale_' . $i['todo'] . '_' . $ok_texte;
723 'version' => denormaliser_version($i['v']),
725 if (isset($i['maj'])) {
726 $trads['version_maj'] = denormaliser_version($i['maj']);
728 $texte = _T($cle_t, $trads);
729 if (is_string($i['done'])) {
730 $texte .= " <span class='$ok_texte'>$i[done]</span>";
732 // si le plugin a ete active dans le meme lot, on remplace le message 'active' par le message 'installe'
733 if ($i['todo'] == 'install' and $ok_texte == 'ok') {
734 $cle_t = 'svp:message_action_finale_' . 'on' . '_' . $ok_texte;
735 $texte_on = _T($cle_t, array(
737 'version' => denormaliser_version($i['v']),
738 'version_maj' => denormaliser_version($i['maj'])
740 if (strpos($done, $texte_on) !== false) {
741 $done = str_replace($texte_on, $texte, $done);
746 $done .= "\t<li class='$ok_texte'>$texte</li>\n";
750 $affiche .= boite_ouvrir(_T('svp:actions_realises'), ($oks ?
'success' : 'notice')) . $done . boite_fermer();
753 if (count($this->end
)) {
755 foreach ($this->end
as $i) {
756 $todo .= "\t<li>" . _T('svp:message_action_' . $i['todo'], array(
758 'version' => denormaliser_version($i['v']),
759 'version_maj' => denormaliser_version($i['maj'])
763 $titre = ($fin ?
_T('svp:actions_non_traitees') : _T('svp:actions_a_faire'));
765 // s'il reste des actions à faire alors que c'est la fin qui est affichée,
766 // on met un lien pour vider. C'est un cas anormal qui peut surgir :
767 // - en cas d'erreur sur une des actions bloquant l'espace privé
768 // - en cas d'appel d'admin_plugins concurrent par le même admin ou 2 admins...
770 include_spip('inc/filtres');
771 if ($this->lock
['time']) {
772 $time = $this->lock
['time'];
776 $date = date('Y-m-d H:i:s', $time);
778 $todo .= "<p class='error'>" . _T('svp:erreur_actions_non_traitees', array(
779 'auteur' => sql_getfetsel('nom', 'spip_auteurs', 'id_auteur=' . sql_quote($this->lock
['id_auteur'])),
780 'date' => affdate_heure($date)
782 $todo .= "<a href='" . parametre_url(self(), 'nettoyer_actions',
783 '1') . "'>" . _T('svp:nettoyer_actions') . "</a>\n";
785 $affiche .= boite_ouvrir($titre, 'notice') . $todo . boite_fermer();
789 include_spip('inc/filtres');
790 $affiche = wrap($affiche, "<div class='svp_retour'>");
797 * Retourne le pourcentage de progression des actions
800 public function progression() {
801 if (count($this->done
)) {
802 return count($this->done
) / (count($this->done
) +
count($this->end
));
808 * Teste l'existance d'un verrou par un auteur ?
810 * Si un id_auteur est transmis, teste que c'est cet auteur
811 * précis qui a posé le verrou.
813 * @see Actionneur::verrouiller()
815 * @param int|string $id_auteur
816 * Identifiant de l'auteur, ou vide
818 * true si un verrou est là, false sinon
820 public function est_verrouille($id_auteur = '') {
821 if ($id_auteur == '') {
822 return ($this->lock
['id_auteur'] ?
true : false);
825 return ($this->lock
['id_auteur'] == $id_auteur);
831 * Un verrou permet de garentir qu'une seule exécution d'actions
832 * est lancé à la fois, ce qui évite que deux administrateurs
833 * puissent demander en même temps des actions qui pourraient
836 * Le verrou est signé par l'id_auteur de l'auteur actuellement identifié.
838 * Le verrou sera sauvegardé en fichier avec la liste des actions
840 * @see Actionneur::sauver_actions()
842 public function verrouiller() {
844 'id_auteur' => $GLOBALS['visiteur_session']['id_auteur'],
852 public function deverrouiller() {
860 * Sauvegarde en fichier cache la liste des actions et le verrou
862 * Crée un tableau contenant les informations principales qui permettront
863 * de retrouver ce qui est à faire comme action, ce qui a été fait,
864 * les erreurs générées, et le verrouillage.
866 * Le cache peut être lu avec la méthode get_actions()
868 * @see Actionneur::get_actions()
870 public function sauver_actions() {
871 $contenu = serialize(array(
872 'todo' => $this->end
,
873 'done' => $this->done
,
874 'work' => $this->work
,
876 'lock' => $this->lock
,
878 ecrire_fichier(_DIR_TMP
. 'stp_actions.txt', $contenu);
882 * Lit le fichier cache de la liste des actions et verrou
884 * Restaure les informations contenues dans le fichier de cache
885 * et écrites avec la méthode sauver_actions().
887 * @see Actionneur::sauver_actions()
889 public function get_actions() {
890 lire_fichier(_DIR_TMP
. 'stp_actions.txt', $contenu);
891 $infos = unserialize($contenu);
892 $this->end
= $infos['todo'];
893 $this->work
= $infos['work'];
894 $this->done
= $infos['done'];
895 $this->err
= $infos['err'];
896 $this->lock
= $infos['lock'];
900 * Nettoyage des actions et verrou
902 * Remet tout à zéro pour pouvoir repartir d'un bon pied.
904 public function nettoyer_actions() {
905 $this->todo
= array();
906 $this->done
= array();
907 $this->work
= array();
908 $this->err
= array();
909 $this->deverrouiller();
910 $this->sauver_actions();
914 * Effectue une des actions qui reste à faire.
916 * Dépile une des actions à faire s'il n'y en a pas en cours
917 * au moment de l'appel et traite cette action
919 * @see Actionneur::do_action()
921 * False si aucune action à faire,
922 * sinon tableau de description courte du paquet + index 'todo' indiquant l'action
924 public function one_action() {
925 // s'il reste des actions, on en prend une, et on la fait
926 // de meme si une action est en cours mais pas terminee (timeout)
927 // on tente de la refaire...
928 if (count($this->end
) or $this->work
) {
929 // on verrouille avec l'auteur en cours pour
930 // que seul lui puisse effectuer des actions a ce moment la
931 if (!$this->est_verrouille()) {
932 $this->verrouiller();
934 // si ce n'est pas verrouille par l'auteur en cours...
935 // ce n'est pas normal, donc on quitte sans rien faire.
936 elseif (!$this->est_verrouille($GLOBALS['visiteur_session']['id_auteur'])) {
940 // si pas d'action en cours
942 // on prend une des actions en attente
943 $this->work
= array_shift($this->end
);
945 $action = $this->work
;
946 $this->sauver_actions();
947 // effectue l'action dans work
950 // si la liste des actions en attente est maintenant vide
951 // on deverrouille aussitot.
952 if (!count($this->end
)) {
953 $this->deverrouiller();
954 $this->sauver_actions();
959 // on ne devrait normalement plus tomber sur un cas de verrouillage ici
960 // mais sait-on jamais. Tester ne couter rien :)
961 if ($this->est_verrouille()) {
962 $this->deverrouiller();
963 $this->sauver_actions();
971 * Effectue l'action en attente.
973 * Appelle une methode do_{todo} de l'Actionneur où todo
974 * est le type d'action à faire.
976 * Place dans la clé 'done' de description courte du paquet
977 * le résultat de l'action (un booléen indiquant si elle s'est bien
980 public function do_action() {
981 if ($do = $this->work
) {
982 $todo = 'do_' . $do['todo'];
983 lire_metas(); // avoir les metas a jour
984 $this->log("Faire $todo avec $do[n]");
985 $do['done'] = $this->$todo($do);
987 $this->work
= array();
988 $this->sauver_actions();
994 * Attraper et activer un paquet
997 * Description courte du paquet
999 * false si erreur, true sinon.
1001 public function do_geton($info) {
1002 if (!$this->tester_repertoire_plugins_auto()) {
1005 $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
1006 if ($dirs = $this->get_paquet_id($i)) {
1007 $this->activer_plugin_dossier($dirs['dossier'], $i);
1012 $this->log("GetOn : Erreur de chargement du paquet " . $info['n']);
1020 * Soit il est là... soit il est à télécharger...
1022 * @param array $info
1023 * Description courte du paquet
1025 * false si erreur, true sinon.
1027 public function do_on($info) {
1028 $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
1030 if (isset($i['id_zone']) and $i['id_zone'] > 0) {
1031 return $this->do_geton($info);
1034 // a activer uniquement
1035 // il faudra prendre en compte les autres _DIR_xx
1036 if (in_array($i['constante'], array('_DIR_PLUGINS', '_DIR_PLUGINS_SUPPL'))) {
1037 $dossier = rtrim($i['src_archive'], '/');
1038 $this->activer_plugin_dossier($dossier, $i, $i['constante']);
1048 * Mettre à jour un paquet
1050 * @param array $info
1051 * Description courte du paquet
1052 * @return bool|array
1054 * description courte du nouveau plugin sinon.
1056 public function do_up($info) {
1057 // ecriture du nouveau
1058 // suppression de l'ancien (si dans auto, et pas au meme endroit)
1059 // OU suppression des anciens fichiers
1060 if (!$this->tester_repertoire_plugins_auto()) {
1064 // $i est le paquet a mettre à jour (donc present)
1065 // $maj est le paquet a telecharger qui est a jour (donc distant)
1067 $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
1069 // on cherche la mise a jour...
1070 // c'est a dire le paquet source que l'on met a jour.
1071 if ($maj = sql_fetsel('pa.*',
1072 array('spip_paquets AS pa', 'spip_plugins AS pl'),
1074 'pl.prefixe=' . sql_quote($info['p']),
1075 'pa.version=' . sql_quote($info['maj']),
1076 'pa.id_plugin = pl.id_plugin',
1077 'pa.id_depot>' . sql_quote(0)
1079 '', 'pa.etatnum DESC', '0,1')
1082 // si dans auto, on autorise à mettre à jour depuis auto pour les VCS
1083 $dir_actuel_dans_auto = '';
1084 if (substr($i['src_archive'], 0, 5) == 'auto/') {
1085 $dir_actuel_dans_auto = substr($i['src_archive'], 5);
1088 if ($dirs = $this->get_paquet_id($maj, $dir_actuel_dans_auto)) {
1089 // Si le plugin a jour n'est pas dans le meme dossier que l'ancien...
1091 // - activer le plugin sur son nouvel emplacement (uniquement si l'ancien est actif)...
1092 // - supprimer l'ancien (si faisable)
1093 if (($dirs['dossier'] . '/') != $i['src_archive']) {
1094 if ($i['actif'] == 'oui') {
1095 $this->activer_plugin_dossier($dirs['dossier'], $maj);
1098 // l'ancien repertoire a supprimer pouvait etre auto/X
1099 // alors que le nouveau est auto/X/Y ...
1100 // il faut prendre en compte ce cas particulier et ne pas ecraser auto/X !
1101 if (substr($i['src_archive'], 0, 5) == 'auto/' and (false === strpos($dirs['dossier'], $i['src_archive']))) {
1102 if (supprimer_repertoire(constant($i['constante']) . $i['src_archive'])) {
1103 sql_delete('spip_paquets', 'id_paquet=' . sql_quote($info['i']));
1108 $this->ajouter_plugin_interessants_meta($dirs['dossier']);
1119 * Mettre à jour et activer un paquet
1121 * @param array $info
1122 * Description courte du paquet
1124 * false si erreur, true sinon
1126 public function do_upon($info) {
1127 $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
1128 if ($dirs = $this->do_up($info)) {
1129 $this->activer_plugin_dossier($dirs['dossier'], $i, $i['constante']);
1139 * Désactiver un paquet
1141 * @param array $info
1142 * Description courte du paquet
1144 * false si erreur, true sinon
1146 public function do_off($info) {
1147 $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
1148 // il faudra prendre en compte les autres _DIR_xx
1149 if (in_array($i['constante'], array('_DIR_PLUGINS', '_DIR_PLUGINS_SUPPL'))) {
1150 include_spip('inc/plugin');
1151 $dossier = rtrim($i['src_archive'], '/');
1152 ecrire_plugin_actifs(array(rtrim($dossier, '/')), false, 'enleve');
1153 sql_updateq('spip_paquets', array('actif' => 'non', 'installe' => 'non'), 'id_paquet=' . sql_quote($info['i']));
1154 $this->actualiser_plugin_interessants();
1155 // ce retour est un rien faux...
1156 // il faudrait que la fonction ecrire_plugin_actifs()
1157 // retourne au moins d'eventuels message d'erreur !
1166 * Désinstaller un paquet
1168 * @param array $info
1169 * Description courte du paquet
1171 * false si erreur, true sinon
1173 public function do_stop($info) {
1174 $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
1175 // il faudra prendre en compte les autres _DIR_xx
1176 if (in_array($i['constante'], array('_DIR_PLUGINS', '_DIR_PLUGINS_SUPPL'))) {
1177 include_spip('inc/plugin');
1178 $dossier = rtrim($i['src_archive'], '/');
1180 $installer_plugins = charger_fonction('installer', 'plugins');
1182 // - false : pas de procedure d'install/desinstalle
1183 // - true : operation deja faite
1184 // - tableau : operation faite ce tour ci.
1185 $infos = $installer_plugins($dossier, 'uninstall', $i['constante']);
1186 if (is_bool($infos) or !$infos['install_test'][0]) {
1187 include_spip('inc/plugin');
1188 ecrire_plugin_actifs(array($dossier), false, 'enleve');
1189 sql_updateq('spip_paquets', array('actif' => 'non', 'installe' => 'non'), 'id_paquet=' . sql_quote($info['i']));
1194 $this->log("Échec de la désinstallation de " . $i['src_archive']);
1197 $this->actualiser_plugin_interessants();
1204 * Effacer les fichiers d'un paquet
1206 * @param array $info
1207 * Description courte du paquet
1209 * false si erreur, true sinon
1211 public function do_kill($info) {
1212 // on reverifie que c'est bien un plugin auto !
1213 // il faudrait aussi faire tres attention sur un site mutualise
1214 // cette option est encore plus delicate que les autres...
1215 $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
1217 if (in_array($i['constante'], array('_DIR_PLUGINS', '_DIR_PLUGINS_SUPPL'))
1218 and substr($i['src_archive'], 0, 5) == 'auto/'
1221 $dir = constant($i['constante']) . $i['src_archive'];
1222 if (supprimer_repertoire($dir)) {
1223 $id_plugin = sql_getfetsel('id_plugin', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
1225 // on supprime le paquet
1226 sql_delete('spip_paquets', 'id_paquet=' . sql_quote($info['i']));
1228 // ainsi que le plugin s'il n'est plus utilise
1229 $utilise = sql_allfetsel(
1231 array('spip_paquets AS pa', 'spip_plugins AS pl'),
1232 array('pa.id_plugin = pl.id_plugin', 'pa.id_plugin=' . sql_quote($id_plugin)));
1234 sql_delete('spip_plugins', 'id_plugin=' . sql_quote($id_plugin));
1236 // on met a jour d'eventuels obsoletes qui ne le sont plus maintenant
1237 // ie si on supprime une version superieure à une autre qui existe en local...
1238 include_spip('inc/svp_depoter_local');
1239 svp_corriger_obsolete_paquets(array($id_plugin));
1242 // on tente un nettoyage jusqu'a la racine de auto/
1243 // si la suppression concerne une profondeur d'au moins 2
1244 // et que les repertoires sont vides
1245 $chemins = explode('/', $i['src_archive']); // auto / prefixe / version
1246 // le premier c'est auto
1247 array_shift($chemins);
1248 // le dernier est deja fait...
1249 array_pop($chemins);
1250 // entre les deux...
1251 while (count($chemins)) {
1253 $dir = constant($i['constante']) . 'auto/' . implode('/', $chemins);
1254 $fichiers = scandir($dir);
1256 foreach ($fichiers as $f) {
1263 // on tente de supprimer si c'est effectivement vide.
1264 if ($vide and !supprimer_repertoire($dir)) {
1267 array_pop($chemins);
1279 * Installer une librairie
1281 * @param array $info
1282 * Description courte du paquet (une librairie ici)
1284 * false si erreur, true sinon
1286 public function do_getlib($info) {
1287 if (!defined('_DIR_LIB') or !_DIR_LIB
) {
1288 $this->err(_T('svp:erreur_dir_dib_indefini'));
1289 $this->log("/!\ Pas de _DIR_LIB defini !");
1293 if (!is_writable(_DIR_LIB
)) {
1294 $this->err(_T('svp:erreur_dir_dib_ecriture', array('dir' => _DIR_LIB
)));
1295 $this->log("/!\ Ne peut pas écrire dans _DIR_LIB !");
1299 if (!autoriser('plugins_ajouter')) {
1300 $this->err(_T('svp:erreur_auth_plugins_ajouter_lib'));
1301 $this->log("/!\ Pas autorisé à ajouter des libs !");
1306 $this->log("Recuperer la librairie : " . $info['n']);
1308 // on recupere la mise a jour...
1309 include_spip('action/teleporter');
1310 $teleporter_composant = charger_fonction('teleporter_composant', 'action');
1311 $ok = $teleporter_composant('http', $info['v'], _DIR_LIB
. $info['n']);
1317 $this->log("Téléporteur en erreur : " . $ok);
1324 * Télécharger un paquet
1326 * @param array $info
1327 * Description courte du paquet
1329 * false si erreur, true sinon
1331 public function do_get($info) {
1332 if (!$this->tester_repertoire_plugins_auto()) {
1336 $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i']));
1338 if ($dirs = $this->get_paquet_id($info['i'])) {
1339 $this->ajouter_plugin_interessants_meta($dirs['dossier']);
1349 * Lancer l'installation d'un paquet
1351 * @param array $info
1352 * Description courte du paquet
1354 * false si erreur, true sinon
1356 public function do_install($info) {
1357 return $this->installer_plugin($info);
1364 * @param string $dossier
1365 * Chemin du répertoire du plugin
1367 * Description en BDD du paquet - row SQL (tableau clé => valeur)
1368 * @param string $constante
1369 * Constante indiquant le chemin de base du plugin (_DIR_PLUGINS, _DIR_PLUGINS_SUPPL, _DIR_PLUGINS_DIST)
1372 public function activer_plugin_dossier($dossier, $i, $constante = '_DIR_PLUGINS') {
1373 include_spip('inc/plugin');
1374 $this->log("Demande d'activation de : " . $dossier);
1376 //il faut absolument que tous les fichiers de cache
1377 // soient inclus avant modification, sinon un appel ulterieur risquerait
1378 // de charger des fichiers deja charges par un autre !
1379 // C'est surtout le ficher de fonction le probleme (options et pipelines
1380 // sont normalement deja charges).
1381 if (@is_readable
(_CACHE_PLUGINS_OPT
)) {
1382 include_once(_CACHE_PLUGINS_OPT
);
1384 if (@is_readable
(_CACHE_PLUGINS_FCT
)) {
1385 include_once(_CACHE_PLUGINS_FCT
);
1387 if (@is_readable
(_CACHE_PIPELINES
)) {
1388 include_once(_CACHE_PIPELINES
);
1391 include_spip('inc/plugin');
1392 ecrire_plugin_actifs(array($dossier), false, 'ajoute');
1393 $installe = $i['version_base'] ?
'oui' : 'non';
1394 if ($installe == 'oui') {
1395 if (!$i['constante']) {
1396 $i['constante'] = '_DIR_PLUGINS';
1398 // installer le plugin au prochain tour
1399 $new_action = array_merge($this->work
, array(
1400 'todo' => 'install',
1401 'dossier' => rtrim($dossier, '/'),
1402 'constante' => $i['constante'],
1403 'v' => $i['version'], // pas forcement la meme version qu'avant lors d'une mise a jour.
1405 array_unshift($this->end
, $new_action);
1406 $this->log("Demande d'installation de $dossier");
1407 #$this->installer_plugin($dossier);
1410 $this->ajouter_plugin_interessants_meta($dossier);
1411 $this->actualiser_plugin_interessants();
1416 * Actualiser les plugins intéressants
1418 * Décrémente chaque score de plugin présent dans la méta
1419 * 'plugins_interessants' et signifiant que ces plugins
1420 * ont été utilisés récemment.
1422 * Les plugins atteignant un score de zéro sont évacués ce la liste.
1424 public function actualiser_plugin_interessants() {
1425 // Chaque fois que l'on valide des plugins,
1426 // on memorise la liste de ces plugins comme etant "interessants",
1427 // avec un score initial, qui sera decremente a chaque tour :
1428 // ainsi un plugin active pourra reter visible a l'ecran,
1429 // jusqu'a ce qu'il tombe dans l'oubli.
1430 $plugins_interessants = @unserialize
($GLOBALS['meta']['plugins_interessants']);
1431 if (!is_array($plugins_interessants)) {
1432 $plugins_interessants = array();
1435 $dossiers = array();
1436 $dossiers_old = array();
1437 foreach ($plugins_interessants as $p => $score) {
1439 $plugins_interessants[$p] = $score;
1440 $dossiers[$p . '/'] = true;
1442 unset($plugins_interessants[$p]);
1443 $dossiers_old[$p . '/'] = true;
1447 // enlever les anciens
1448 if ($dossiers_old) {
1449 // ATTENTION, il faudra prendre en compte les _DIR_xx
1450 sql_updateq('spip_paquets', array('recent' => 0), sql_in('src_archive', array_keys($dossiers_old)));
1453 $plugs = sql_allfetsel('src_archive', 'spip_paquets', 'actif=' . sql_quote('oui'));
1454 $plugs = array_map('array_shift', $plugs);
1455 foreach ($plugs as $dossier) {
1456 $dossiers[$dossier] = true;
1457 $plugins_interessants[rtrim($dossier, '/')] = 30; // score initial
1460 $plugs = sql_updateq('spip_paquets', array('recent' => 1), sql_in('src_archive', array_keys($dossiers)));
1461 ecrire_meta('plugins_interessants', serialize($plugins_interessants));
1466 * Ajoute un plugin dans les plugins intéressants
1468 * Initialise à 30 le score du plugin indiqué par le chemin transmis,
1469 * dans la liste des plugins intéressants.
1471 * @param string $dir
1472 * Chemin du répertoire du plugin
1474 public function ajouter_plugin_interessants_meta($dir) {
1475 $plugins_interessants = @unserialize
($GLOBALS['meta']['plugins_interessants']);
1476 if (!is_array($plugins_interessants)) {
1477 $plugins_interessants = array();
1479 $plugins_interessants[$dir] = 30;
1480 ecrire_meta('plugins_interessants', serialize($plugins_interessants));
1484 * Lancer l'installation d'un plugin
1486 * @param array $info
1487 * Description courte du paquet
1489 * false si erreur, true sinon
1491 public function installer_plugin($info) {
1492 // il faut info['dossier'] et info['constante'] pour installer
1493 if ($plug = $info['dossier']) {
1494 $installer_plugins = charger_fonction('installer', 'plugins');
1495 $infos = $installer_plugins($plug, 'install', $info['constante']);
1497 // en absence d'erreur, on met a jour la liste des plugins installes...
1498 if (!is_array($infos) or $infos['install_test'][0]) {
1499 $meta_plug_installes = @unserialize
($GLOBALS['meta']['plugin_installes']);
1500 if (!$meta_plug_installes) {
1501 $meta_plug_installes = array();
1503 $meta_plug_installes[] = $plug;
1504 ecrire_meta('plugin_installes', serialize($meta_plug_installes), 'non');
1507 if (!is_array($infos)) {
1508 // l'installation avait deja ete faite un autre jour
1511 // l'installation est neuve
1512 list($ok, $trace) = $infos['install_test'];
1516 // l'installation est en erreur
1517 $this->err(_T('svp:message_action_finale_install_fail',
1518 array('plugin' => $info['n'], 'version' => denormaliser_version($info['v']))) . "<br />" . $trace);
1528 * Télécharge un paquet
1530 * Supprime les fichiers obsolètes (si présents)
1532 * @param int|array $id_or_row
1533 * Identifiant du paquet ou description ligne SQL du paquet
1534 * @param string $dest_ancien
1535 * Chemin vers l'ancien répertoire (pour les mises à jour par VCS)
1536 * @return bool|array
1538 * Tableau de 2 index sinon :
1539 * - dir : Chemin du paquet téléchargé depuis la racine
1540 * - dossier : Chemin du paquet téléchargé, depuis _DIR_PLUGINS
1542 public function get_paquet_id($id_or_row, $dest_ancien = "") {
1543 // on peut passer direct le row sql...
1544 if (!is_array($id_or_row)) {
1545 $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($id_or_row));
1551 if ($i['nom_archive'] and $i['id_depot']) {
1552 $this->log("Recuperer l'archive : " . $i['nom_archive']);
1553 // on récupère les informations intéressantes du dépot :
1554 // - url des archives
1555 // - éventuellement : type de serveur (svn, git) et url de la racine serveur (svn://..../)
1556 $adresses = sql_fetsel(array('url_archives', 'type', 'url_serveur'), 'spip_depots',
1557 'id_depot=' . sql_quote($i['id_depot']));
1558 if ($adresses and $adresse = $adresses['url_archives']) {
1560 // destination : auto/prefixe/version (sinon auto/nom_archive/version)
1561 $prefixe = sql_getfetsel('pl.prefixe',
1562 array('spip_paquets AS pa', 'spip_plugins AS pl'),
1563 array('pa.id_plugin = pl.id_plugin', 'pa.id_paquet=' . sql_quote($i['id_paquet'])));
1566 $base = ($prefixe ?
strtolower($prefixe) : substr($i['nom_archive'], 0, -4)); // enlever .zip ...
1569 $dest_future = $base . '/v' . denormaliser_version($i['version']);
1571 // Nettoyer les vieux formats dans auto/
1572 if ($this->tester_repertoire_destination_ancien_format(_DIR_PLUGINS_AUTO
. $base)) {
1573 supprimer_repertoire(_DIR_PLUGINS_AUTO
. $base);
1576 // l'url est différente en fonction du téléporteur
1577 $teleporteur = $this->choisir_teleporteur($adresses['type']);
1578 if ($teleporteur == 'http') {
1579 $url = $adresse . '/' . $i['nom_archive'];
1580 $dest = $dest_future;
1582 $url = $adresses['url_serveur'] . '/' . $i['src_archive'];
1583 $dest = $dest_ancien ?
$dest_ancien : $dest_future;
1586 // on recupere la mise a jour...
1587 include_spip('action/teleporter');
1588 $teleporter_composant = charger_fonction('teleporter_composant', 'action');
1589 $ok = $teleporter_composant($teleporteur, $url, _DIR_PLUGINS_AUTO
. $dest);
1591 // pour une mise à jour via VCS, il faut rebasculer sur le nouveau nom de repertoire
1592 if ($dest != $dest_future) {
1593 rename(_DIR_PLUGINS_AUTO
. $dest, _DIR_PLUGINS_AUTO
. $dest_future);
1597 'dir' => _DIR_PLUGINS_AUTO
. $dest,
1598 'dossier' => 'auto/' . $dest, // c'est depuis _DIR_PLUGINS ... pas bien en dur...
1602 $this->log("Téléporteur en erreur : " . $ok);
1604 $this->log("Aucune adresse pour le dépot " . $i['id_depot']);
1613 * Teste que le répertoire plugins auto existe et
1614 * que l'on peut ecrire dedans !
1617 * True si on peut écrire dedans, false sinon
1619 public function tester_repertoire_plugins_auto() {
1620 include_spip('inc/plugin'); // pour _DIR_PLUGINS_AUTO
1621 if (!defined('_DIR_PLUGINS_AUTO') or !_DIR_PLUGINS_AUTO
) {
1622 $this->err(_T('svp:erreur_dir_plugins_auto_indefini'));
1623 $this->log("/!\ Pas de _DIR_PLUGINS_AUTO defini !");
1627 if (!is_writable(_DIR_PLUGINS_AUTO
)) {
1628 $this->err(_T('svp:erreur_dir_plugins_auto_ecriture', array('dir' => _DIR_PLUGINS_AUTO
)));
1629 $this->log("/!\ Ne peut pas écrire dans _DIR_PLUGINS_AUTO !");
1639 * Teste si un répertoire du plugin auto, contenant un plugin
1640 * est dans un ancien format auto/prefixe/ (qui doit alors être supprimé)
1641 * ou dans un format normal auto/prefixe/vx.y.z
1644 * $this->tester_repertoire_destination_ancien_format(_DIR_PLUGINS_AUTO . $base);
1646 * @param string $dir_dans_auto
1647 * Chemin du répertoire à tester
1649 * true si le répertoire est dans un ancien format
1651 public function tester_repertoire_destination_ancien_format($dir_dans_auto) {
1652 // si on tombe sur un auto/X ayant des fichiers (et pas uniquement des dossiers)
1653 // ou un dossier qui ne commence pas par 'v'
1654 // c'est que auto/X n'était pas chargé avec SVP
1655 // ce qui peut arriver lorsqu'on migre de SPIP 2.1 à 3.0
1656 // dans ce cas, on supprime auto X pour mettre notre nouveau paquet.
1657 if (is_dir($dir_dans_auto)) {
1658 $base_files = scandir($dir_dans_auto);
1659 if (is_array($base_files)) {
1660 $base_files = array_diff($base_files, array('.', '..'));
1661 foreach ($base_files as $f) {
1662 if (($f[0] != '.' and $f[0] != 'v') // commence pas par v
1663 or ($f[0] != '.' and !is_dir($dir_dans_auto . '/' . $f))
1664 ) { // commence par v mais pas repertoire
1676 * Teste s'il est possible d'utiliser un téléporteur particulier,
1677 * sinon retourne le nom du téléporteur par défaut
1679 * @param string $teleporteur Téléporteur VCS à tester
1680 * @param string $defaut Téléporteur par défaut
1682 * @return string Nom du téléporteur à utiliser
1684 public function choisir_teleporteur($teleporteur, $defaut = 'http') {
1685 // Utiliser un teleporteur vcs si possible si demandé
1686 if (defined('SVP_PREFERER_TELECHARGEMENT_PAR_VCS') and SVP_PREFERER_TELECHARGEMENT_PAR_VCS
) {
1688 include_spip('teleporter/' . $teleporteur);
1689 $tester_teleporteur = "teleporter_{$teleporteur}_tester";
1690 if (function_exists($tester_teleporteur)) {
1691 if ($tester_teleporteur()) {
1692 return $teleporteur;
1703 * Teste si le plugin SVP (celui-ci donc) a
1704 * été désinstallé / désactivé dans les actions réalisées
1707 * On ne peut tester sa désactivation que dans le hit où la désinstallation
1708 * est réalisée, puisque après, s'il a été désactivé, au prochain hit
1709 * on ne connaîtra plus ce fichier !
1712 * true si SVP a été désactivé, false sinon
1714 public function tester_si_svp_desactive() {
1715 foreach ($this->done
as $d) {
1716 if ($d['p'] == 'SVP'
1717 and $d['done'] == true
1718 and in_array($d['todo'], array('off', 'stop'))
1731 * Gère le traitement des actions des formulaires utilisant l'Actionneur
1733 * @param array $actions
1734 * Liste des actions a faire (id_paquet => action)
1735 * @param array $retour
1736 * Tableau de retour du CVT dans la partie traiter
1737 * @param string $redirect
1741 function svp_actionner_traiter_actions_demandees($actions, &$retour, $redirect = null) {
1742 $actionneur = new Actionneur();
1743 $actionneur->ajouter_actions($actions);
1744 $actionneur->verrouiller();
1745 $actionneur->sauver_actions();
1747 $redirect = $redirect ?
$redirect : generer_url_ecrire('admin_plugin');
1748 $retour['redirect'] = generer_url_action('actionner', 'redirect=' . urlencode($redirect));
1749 set_request('_todo', '');
1750 $retour['message_ok'] = _T("svp:action_patienter");