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

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

Pétouilles oubliées évidemment :)

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