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

Last change on this file since 52860 was 52860, checked in by kent1@…, 9 years ago

if (!defined("_ECRIRE_INC_VERSION")) return; sur tout fichier PHP pour sécurité future principalement

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