source: spip-zone/_plugins_/step/inc/step_decideur.php @ 34255

Last change on this file since 34255 was 34255, checked in by marcimat@…, 10 years ago

Mettre à jour la todolist

File size: 15.7 KB
Line 
1<?php
2
3class Decideur {
4
5        // plugins actifs en cours avant toute modification
6        var $start = array(
7                'i' => array(),
8                'p' => array(),
9        );
10       
11        // plugins actifs a la fin des modifications effectuees
12        var $end = array(
13                'i' => array(),
14                'p' => array(),
15        );
16       
17        var $ask = array();     // toutes les actions a faire demandees
18        var $todo = array();    // toutes les actions a faire
19        var $changes = array(); // juste les actions a faire en plus de celles demandees
20        var $off = array();     // juste les plugins a arreter
21        var $invalides = array(); // juste les plugins invalides (suite a des dependances introuvables)
22        var $mefiance = false;  // lorsqu'une action entraine des desactivations, mettre ce flag a true !
23       
24        var $err = array(); // erreurs rencontrees
25        var $ok = true;     // le resultat permet d'effectuer toutes les actions
26        var $log = false;   // loguer les differents elements
27       
28         
29        function Decideur () {}
30
31       
32        /* Liste des plugins deja actifs */
33        function liste_plugins_actifs() {
34                return $this->infos_courtes('actif='.sql_quote('oui'));
35        }
36
37        function log($quoi) {
38                if ($this->log) {
39                        spip_log($quoi,'decideur');
40                }
41        }
42       
43        function infos_courtes_id($id) {
44                // on cache ceux la
45                static $plug = array();
46                if (!isset($plug[$id])) {
47                        $plug[$id] = $this->infos_courtes('id_plugin=' . sql_quote($id));
48                }
49                return $plug[$id];
50        }
51       
52        /*
53         * recuperer les infos utiles des plugins
54         * on passe un where et on cree deux tableaux
55         * id (infos)
56         * prefixe (infos)
57         * OU prefixe[] (infos) si multiple=true, classes par etats decroissants.
58         *
59         */
60        function infos_courtes($where, $multiple=false) {
61                $plugs = array(
62                        'i'=>array(),
63                        'p'=>array()
64                );
65               
66                $orderby = $multiple ? 'etatnum DESC' : '';
67               
68                $res = sql_select(array(
69                        'id_plugin AS i',
70                        'nom AS n',
71                        'prefixe AS p',
72                        'version AS v',
73                        'etatnum AS e',
74                        'dependances',
75                        'maj_version AS maj',
76                        'actif AS a'), 'spip_plugins', $where, '', $orderby);
77                while ($r = sql_fetch($res)) {
78                        $d = unserialize($r['dependances']);
79                        // voir pour enregistrer en bdd simplement 'n' et 'u' (pas la peine d'encombrer)...
80                        if (!$d) $d = array('necessite'=>array(), 'utilise'=>array());
81                        unset($r['dependances']);
82                       
83                        $plugs['i'][$r['i']] = $r;
84                        $plugs['i'][$r['i']]['dn'] = $d['necessite'];
85                        $plugs['i'][$r['i']]['du'] = $d['utilise'];     
86
87                        if ($multiple) {
88                                $plugs['p'][$r['p']][] = &$plugs['i'][$r['i']]; // alias
89                        } else {
90                                $plugs['p'][$r['p']] = &$plugs['i'][$r['i']]; // alias
91                        }
92                }
93                return $plugs;
94        }
95
96
97        /* liste des erreurs */
98        function erreur($id, $texte = '') {
99                $this->log("erreur: $id -> $texte");
100                if (!is_array($this->err[$id])) $this->err[$id] = array();
101                $this->err[$id][] = $texte;
102                $this->ok = false;
103        }
104
105        function en_erreur($id) {
106                return isset($this->err[$id]) ? $this->err[$id] : false;
107        }
108
109
110        /* verifier qu'on plugin plus recent existe pour un prefixe et une version donnee */
111        function chercher_plugin_recent($prefixe, $version) {
112                $news = $this->infos_courtes(array('prefixe=' . sql_quote($prefixe), 'obsolete=' . sql_quote('non'), 'id_zone>'.sql_quote(0)), true);
113                $res = false;
114                if ($news and count($news['p'][$prefixe]) > 0) {
115                        foreach ($news['p'][$prefixe] as $new) {
116                                if (version_compare($new['v'],$version,'>')) {
117                                        if (!$res or version_compare($new['v'],$res['v'],'>')) {
118                                                $res = $new;
119                                        }
120                                }
121                        }
122                }
123                return $res;
124        }
125       
126        /* verifier qu'un plugin exsite avec prefixe (cfg) pour une version [1.0;] donnee */
127        function chercher_plugin_compatible($prefixe, $version) {
128                $news = $this->infos_courtes(array('prefixe=' . sql_quote($prefixe), 'obsolete=' . sql_quote('non')), true);
129                if ($news and count($news['p'][$prefixe]) > 0) {
130                        foreach ($news['p'][$prefixe] as $new) {
131                                if (step_plugin_version_compatible($version, $new['v'])) {
132                                        return $new;
133                                }
134                        }
135                }
136                return false;
137        }
138
139
140        // ajouter a la liste des plugins actifs
141        function add($info) {
142                $this->end['i'][$info['i']] = $info;
143                $this->end['p'][$info['p']] = &$this->end['i'][$info['i']];
144        }
145
146        function off($info, $recur = false) {
147                $this->log('- stopper ' . $info['p']);
148                $this->remove($info);
149                $this->off[$info['p']] = $info;
150                // si recursif, on stoppe aussi les plugins dependants
151                if ($recur) {
152                        foreach ($this->end['i'] as $id => $plug) {     
153                                if (is_array($plug['dn']) and $plug['dn']) {
154                                        foreach ($plug['dn'] as $n) {
155                                                if ($info['p'] == $n['id']) {
156                                                        $this->change($plug, 'off');
157                                                        $this->off($plug, true);
158                                                        $this->mefiance = true;
159                                                }
160                                        }
161                                }
162                        }
163                }       
164        }
165
166       
167        function sera_off($prefixe) {
168                return isset($this->off[$prefixe]) ? $this->off[$prefixe] : false;
169        }
170
171        function sera_off_id($id) {
172                foreach ($this->off as $info) {
173                        if ($info['i'] == $id) {
174                                return $info;
175                        }
176                }
177                return false;
178        }       
179
180        function sera_actif($prefixe) {
181                return isset($this->end['p'][$prefixe]) ? $this->end['p'][$prefixe] : false;
182        }
183
184        function sera_actif_id($id) {
185                return isset($this->end['i'][$id]) ? $this->end['i'][$id] : false;
186        }
187       
188        // ajouter a la liste des demandes
189        function ask($info, $quoi) {
190                $this->ask[$info['i']] = $info;
191                $this->ask[$info['i']]['todo'] = $quoi;
192                $this->todo($info, $quoi);
193        }
194       
195        // ajouter a la liste des changements en plus
196        function change($info, $quoi) {
197                $this->changes[$info['i']] = $info;
198                $this->changes[$info['i']]['todo'] = $quoi;
199                $this->todo($info, $quoi);
200        }
201
202        // pour annuler une action (automatique) qui finalement etait
203        // reellement officielement demandee (cas de mise a 'off' de plugins).
204        function annule_change($info) {
205                unset($this->changes[$info['i']]);
206        }
207       
208        // ajouter a la liste des actions
209        function todo($info, $quoi) {
210                $this->todo[$info['i']] = $info;
211                $this->todo[$info['i']]['todo'] = $quoi;
212        }
213
214        // retirer un plugin des actifs
215        function remove($info) {
216                $i = $this->end['p'][$info['p']]; // aucazou ce ne soit pas les memes ids
217                unset($this->end['i'][$info['i']], $this->end['p'][$info['p']], $this->end['i'][$i['i']]);
218        }
219
220        // invalider un plugin...
221        function invalider($info) {
222                $this->log("-> invalider $info[p]");
223                $this->remove($info); // suffisant ?
224                $this->invalides[$info['p']] = $info;
225                $this->annule_change($info);
226                unset($this->todo[$info['i']]);
227        }
228
229        function sera_invalide($p) {
230                return isset($this->invalides[$p]) ? $this->invalides[$p] : false;
231        }
232
233
234        function est_presente_lib($lib) {
235                static $libs = false;
236                if ($libs === false) {
237                        include_spip('inc/step');
238                        $libs = step_lister_librairies();
239                }
240                return isset($libs[$lib]) ? $libs[$lib] : false;
241        }
242
243
244        /* Ajouter les actions demandees */
245        function actionner($todo = null) {
246                if (is_array($todo)) {
247                        foreach ($todo as $id => $t) {
248                                // plusieurs choses nous interessent... Sauf... le simple telechargement
249                                // et la suppression des fichiers (qui ne peuvent etre fait
250                                // que si le plugin n'est pas actif)
251                                $this->log("-- todo: $id/$t");
252
253                                switch ($t) {
254                                        case 'on':
255                                                // ajouter ce plugin dans la liste
256                                                if (!$this->sera_actif_id($id)) {
257                                                        $i = $this->infos_courtes_id($id);
258                                                        if ($i['i'][$id]) {
259                                                                $this->add($i['i'][$id]);
260                                                                $this->ask($i['i'][$id], 'on');
261                                                        } else {
262                                                                // la c'est vraiment pas normal... Erreur plugin inexistant...
263                                                                // concurrence entre administrateurs ?
264                                                                $this->erreur($id, _L("Le plugin demand&eacute; est inexistant ! ($id)"));
265                                                        }
266                                                }
267                                                break;
268                                        case 'up':
269                                        case 'upon':
270                                                // le plugin peut etre actif !
271                                                // ajouter ce plugin dans la liste et retirer l'ancien
272                                                $i = $this->infos_courtes_id($id);
273                                                if ($i = $i['i'][$id]) {
274                                                        // new : plugin a installer
275                                                        if ($new = $this->chercher_plugin_recent($i['p'], $i['v'])) {
276                                                                // ajouter seulement si on l'active !
277                                                                // ou si le plugin est actuellement actif
278                                                                if ($t == 'upon' or $this->sera_actif_id($id)) {
279                                                                        $this->remove($i);
280                                                                        $this->add($new);
281                                                                }
282                                                                $this->ask($i, $t);
283                                                        } else {
284                                                                // on n'a pas trouve la nouveaute !!!
285                                                                $this->erreur($id, _L("Mise &agrave; jour introuvable du plugin $i[p] ! ($id)"));
286                                                        }
287                                                } else {
288                                                        // mauvais identifiant ?
289                                                        // on n'a pas trouve le plugin !!!
290                                                        $this->erreur($id, _L("Mise &agrave; jour impossible d'un plugin inconnu ! ($id)"));
291                                                }
292                                                break;
293                                        case 'off':
294                                        case 'stop':
295                                                // retirer ce plugin
296                                                // (il l'est peut etre deja)
297                                                if ($info = $this->sera_actif_id($id)
298                                                or  $info_off = $this->sera_off_id($id)) {
299                                                        // annuler le signalement en "proposition" (due a une mise a 'off' recursive)
300                                                        // de cet arret de plugin, vu qu'on de demande reellement
301                                                        if (!$info) {
302                                                                $info = $info_off;
303                                                                $this->annule_change($info);
304                                                        }
305                                                        $this->ask($info, $t);
306                                                        $this->todo($info, $t);
307                                                        $this->off($info, true);
308                                                       
309                                                } else {
310                                                        // pas normal... plugin deja inactif...
311                                                        // concurrence entre administrateurs ?
312                                                        $this->erreur($id, _L("Impossible de d&eacute;sactiver un plugin non actif !"));                                       
313                                                }
314                                                break;
315                                        case 'null':
316                                        case 'get':
317                                        case 'kill':
318                                                if ($info = $this->infos_courtes_id($id)) {
319                                                        $this->ask($info['i'][$id], $t);
320                                                } else {
321                                                        // pas normal... plugin inconnu... concurrence entre administrateurs ?
322                                                        $this->erreur($id, _L("Impossible de trouver le plugin $id pour action $t !"));
323                                                }
324                                                break;
325                                }
326                        }
327                }
328                return $this->ok;
329        }
330
331
332        // ecrire les plugins actifs
333        function start() {
334                $this->start = $this->end = $this->liste_plugins_actifs();
335        }
336       
337        /* Calcul de dependances */
338        function verifier_dependances($todo = null) {
339               
340                $this->start();
341
342                // ajouter les actions
343                if (!$this->actionner($todo)) {
344                        $this->log("! Todo en echec !");
345                        $this->log($decideur->err);
346                        return false;
347                }
348
349                // doit on reverifier les dependances ?
350                // oui des qu'on modifie quelque chose...
351                // attention a ne pas boucler infiniment !
352               
353                $supersticieux = 0;
354                do {
355                        $try_again = 0;
356                        $supersticieux++;
357
358                        // verifier chaque dependance de chaque plugin a activer
359                        foreach ($this->end['i'] as $info) {
360                                if (!$this->verifier_dependances_plugin($info)) {
361                                        $try_again = true;
362                                }
363                        }
364                        unset($id, $info);
365                        $this->log("--------> try_again: $try_again, supersticieux: $supersticieux");
366                } while ($try_again > 0 and $supersticieux < 100); # and !count($this->err)
367
368                $this->log("Fin !");
369                $this->log("Ok: " . $this->ok);
370                # $this->log($this->todo);
371               
372                return $this->ok;
373        }
374
375
376
377        function verifier_dependances_plugin($info, $prof=0) {
378                $this->log("- [$prof] verifier dependances " . $info['p']);
379                $id = $info['i'];
380               
381                $cache = array(); // cache des actions realisees dans ce tour
382               
383                if (is_array($info['dn']) and $info['dn']) {
384                        foreach ($info['dn'] as $n) {
385                                // de deux choses l'une...
386                                // soit la dependance est a SPIP, soit a un plugin, soit a une librairie...
387
388                                $p = $n['id'];
389                                $v = $n['version'];
390                               
391                                // si c'est a SPIP et qu'on ne valide pas, on retourne une erreur !
392                                // normalement, on ne devrait pas trop pouvoir tomber sur ce cas                               
393                                if (strtoupper($p) == 'SPIP') {
394                                        if (!step_verifier_plugin_compatible_version_spip($v)) {
395                                                $this->invalider($info);
396                                                $this->erreur($id, _L("$info[p] n'est pas compatible avec cette version de SPIP !"));   
397                                                // est-ce qu'on quitte tout de suite, ou teste-t-on tout ?
398                                                // pour l'instant, essayons de tout tester quand meme
399                                                // nous verrons par la suite si c'est judicieux ou pas
400                                        }
401                                } elseif (strpos($p,'lib:')===0) {
402                                        $lib = substr($p, 4);
403                                        // l'identifiant commence par "lib:", c'est une librairie dont il s'agit.
404                                        // on verifie sa presence OU le fait qu'on pourra la telecharger
405                                        if ($lib and !$this->est_presente_lib($lib)) {
406                                                // peut on ecrire ?
407                                                if (!is_writable(_DIR_LIB)) {
408                                                        $this->invalider($info);
409                                                        $this->erreur($id, _L("&laquo;$info[p]&raquo; a besion de la librairie <a href='$n[src]'>$lib</a>
410                                                                plac&eacute; dans le r&eacute;pertoire <var>lib/</var> &agrave; la racine de votre SPIP.
411                                                                Cependant, ce r&eacute;pertoire n'est pas accessible en &eacute;criture.
412                                                                Vous devez l'installer manuellement ou donner des permissions d'&eacute;criture
413                                                                &agrave; ce r&eacute;pertoire."));     
414                                                }
415                                        }
416                                       
417                                } else {
418                                        $this->log("-- verifier $p");
419                                        // nous sommes face a une dependance de plugin
420                                        // on regarde s'il est present et a la bonne version
421                                        // sinon on le cherche et on l'ajoute
422                                        if (!($ninfo = $this->sera_actif($p)
423                                        and !$err = $this->en_erreur($ninfo['i'])
424                                        and step_plugin_version_compatible($v, $ninfo['v']))) {
425                                               
426                                                // absent ou erreur ou pas compatible
427                                                $etat = $err ? 'erreur' : ($ninfo ? 'conflit' : 'absent');
428                                                $this->log("Dedendance " . $p . " &agrave; resoudre ! ($etat)");
429
430                                                switch ($etat) {
431                                                        // commencons par le plus simple :
432                                                        // en cas d'absence, on cherche ou est ce plugin !
433                                                        case 'absent':
434                                                                // on choisit par defaut le meilleur etat de plugin.
435                                                                // (attention: la on ne regarde pas si c'est local ou distant... a corriger ?)
436                                                                if (!$this->sera_off($p)
437                                                                and $new = $this->chercher_plugin_compatible($p, $v)
438                                                                and $this->verifier_dependances_plugin($new, ++$prof)) {
439                                                                        // si le plugin existe localement, c'est que
440                                                                        // c'est une mise a jour + activation a faire
441                                                                        $cache[] = $new;       
442                                                                        $i = $this->infos_courtes(array(
443                                                                                        'prefixe=' . sql_quote($new['p']),
444                                                                                        'maj_version=' . sql_quote($new['v'])
445                                                                                ), true);
446                                                                        if (isset($i['p'][$new['p']]) and count($i['p'][$new['p']])) {
447                                                                                // c'est une mise a jour
448                                                                                $vieux = $i['p'][$new['p']][0];
449                                                                                $this->change($vieux, 'upon');
450                                                                                $this->log("-- update+active : $p");
451                                                                        } else {
452                                                                                // tout nouveau tout beau
453                                                                                $this->change($new, 'on');
454                                                                                $this->log("-- nouveau : $p");
455                                                                        }
456                                                                        $this->add($new);
457                                                                } else {
458                                                                        $this->log("-- !erreur : $p");
459                                                                        // on ne trouve pas la dependance !
460                                                                        $this->invalider($info);
461                                                                        $this->erreur($id, _L("Le plugin " . $info['p'] . " d&eacute;pend de " . $p . ($v ? " en version " . $v : '') ));
462                                                                }
463                                                                unset($new, $vieux);
464                                                                break;
465
466                                                        case 'erreur':
467                                                                break;
468
469                                                        // present, mais conflit de version
470                                                        // de deux choses l'une :
471                                                        // soit on trouve un paquet meilleur...
472                                                        // soit pas :)
473                                                        case 'conflit':
474                                                                $this->log("  conflit -> demande $v, present : " . $ninfo['v']);
475                                                                if (!$this->sera_off($p)
476                                                                and $new = $this->chercher_plugin_compatible($p, $v)
477                                                                and $this->verifier_dependances_plugin($new, ++$prof)) {
478                                                                        // on connait le nouveau...
479                                                                        $cache[] = $new;
480                                                                        $this->remove($ninfo);
481                                                                        $this->add($new);
482                                                                        $this->change($ninfo,'up');
483                                                                        $this->log("-- update : $p");
484                                                                } else {
485                                                                        $this->log("-- !erreur : $p");
486                                                                        // on ne trouve pas la dependance !
487                                                                        $this->invalider($info);
488                                                                        $this->erreur($id, _L("Le plugin " . $info['p'] . " dépend de " . $p  . ($v ? " en version " . $v : '') ));
489                                                                }                                                                       
490                                                                break;
491                                                }
492
493                                        } else {
494                                                $this->log('-- dep OK pour '.$info['p'].' : '.$p);
495                                        }
496                                }
497                               
498                                if ($this->sera_invalide($info['p'])) {
499                                        break;
500                                }
501                        }
502                        unset($n, $v, $p, $ninfo, $present, $conflit, $erreur, $err);
503
504                        // si le plugin est devenu invalide...
505                        // on invalide toutes les actions qu'on vient de faire !
506                        if ($this->sera_invalide($info['p'])) {
507                                $this->log("> Purge du cache");
508                                foreach ($cache as $i) {
509                                        $this->invalider($i);
510                                }
511                                return false;
512                        }
513                }
514                return true;
515        }
516
517
518
519
520
521        function presenter_actions($quoi) {
522                $res = array();
523                foreach ($this->$quoi as $id=>$info) {
524                        $supp = ($info['todo'] == 'up' or $info['todo'] == 'upon') ? 'en version ' . $info['maj'] : '';
525                        $res[] = "$info[todo] : $info[p] $info[v] $supp";
526                }
527                return $res;
528        }
529}
530
531
532
533
534
535
536
537
538
539
540
541
542?>
Note: See TracBrowser for help on using the repository browser.