~maj v3.0.19-->v3.0.21
[ptitvelo/web/www.git] / www / ecrire / inc / plugin.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2014 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 if (!defined('_ECRIRE_INC_VERSION')) return;
14
15 // l'adresse du repertoire de telechargement et de decompactage des plugins
16 define('_DIR_PLUGINS_AUTO', _DIR_PLUGINS.'auto/');
17
18 #include_spip('inc/texte'); // ????? Appelle public/parametrer trop tot avant la reconstruction du chemin des plugins.
19 include_spip('plugins/installer');
20
21 // lecture des sous repertoire plugin existants
22 // $dir_plugins pour forcer un repertoire (ex: _DIR_PLUGINS_DIST)
23 // _DIR_PLUGINS_SUPPL pour aller en chercher ailleurs
24 // (chemins relatifs a la racine du site, separes par des ":")
25 // http://doc.spip.org/@liste_plugin_files
26 function liste_plugin_files($dir_plugins = null){
27 static $plugin_files=array();
28 if (is_null($dir_plugins))
29 $dir_plugins = _DIR_PLUGINS;
30 if (!isset($plugin_files[$dir_plugins])
31 OR count($plugin_files[$dir_plugins]) == 0){
32 $plugin_files[$dir_plugins] = array();
33 foreach (fast_find_plugin_dirs($dir_plugins) as $plugin) {
34 $plugin_files[$dir_plugins][] = substr($plugin,strlen($dir_plugins));
35 }
36
37 sort($plugin_files[$dir_plugins]);
38 // et on lit le XML de tous les plugins pour le mettre en cache
39 // et en profiter pour nettoyer ceux qui n'existent plus du cache
40 $get_infos = charger_fonction('get_infos','plugins');
41 $get_infos($plugin_files[$dir_plugins],false,$dir_plugins,true);
42 }
43 return $plugin_files[$dir_plugins];
44 }
45
46 function fast_find_plugin_dirs($dir, $max_prof=100) {
47 $fichiers = array();
48 // revenir au repertoire racine si on a recu dossier/truc
49 // pour regarder dossier/truc/ ne pas oublier le / final
50 $dir = preg_replace(',/[^/]*$,', '', $dir);
51 if ($dir == '') $dir = '.';
52
53 if (!is_dir($dir))
54 return $fichiers;
55 if (is_plugin_dir($dir,'')) {
56 $fichiers[] = $dir;
57 return $fichiers;
58 }
59 if ($max_prof<=0)
60 return $fichiers;
61
62 $subdirs = array();
63 if (@is_dir($dir) AND is_readable($dir) AND $d = @opendir($dir)) {
64 while (($f = readdir($d)) !== false) {
65 if ($f[0] != '.' # ignorer . .. .svn etc
66 AND $f != 'CVS'
67 AND is_dir($f = "$dir/$f"))
68 $subdirs[] = $f;
69 }
70 closedir($d);
71 }
72
73 foreach($subdirs as $d){
74 $fichiers = array_merge($fichiers,fast_find_plugin_dirs("$d/",$max_prof-1));
75 }
76 return $fichiers;
77 }
78
79 function is_plugin_dir($dir,$dir_plugins = null){
80 if (is_array($dir)){
81 foreach($dir as $k=>$d){
82 if (!is_plugin_dir($d,$dir_plugins))
83 unset($dir[$k]);
84 }
85 return $dir;
86 }
87 if (is_null($dir_plugins))
88 $dir_plugins = _DIR_PLUGINS;
89 $search = array("$dir_plugins$dir/plugin.xml","$dir_plugins$dir/paquet.xml");
90
91 foreach($search as $s){
92 if (file_exists($s)){
93 return $dir;
94 }
95 }
96 return '';
97 }
98
99 // Regexp d'extraction des informations d'un intervalle de compatibilité
100 define('_EXTRAIRE_INTERVALLE', ',^[\[\(\]]([0-9.a-zRC\s\-]*)[;]([0-9.a-zRC\s\-\*]*)[\]\)\[]$,');
101
102 /**
103 * Teste si le numéro de version d'un plugin est dans un intervalle donné.
104 *
105 * Cette fonction peut être volontairement trompée (phase de développement) :
106 * voir commentaire infra sur l'utilisation de la constante _DEV_PLUGINS
107 *
108 * @param string $intervalle
109 * Un intervalle entre 2 versions. ex: [2.0.0-dev;2.1.*]
110 * @param string $version
111 * Un numéro de version. ex: 3.1.99]
112 * @param string $avec_quoi
113 * Ce avec quoi est testée la compatibilité. par défaut ('')
114 * avec un plugin (cas des 'necessite'), parfois ('spip')
115 * avec SPIP.
116 * @return bool
117 * True si dans l'intervalle, false sinon.
118 **/
119 function plugin_version_compatible($intervalle, $version, $avec_quoi = '') {
120
121 if (!strlen($intervalle)) return true;
122 if (!preg_match(_EXTRAIRE_INTERVALLE,$intervalle,$regs)) return false;
123 // Extraction des bornes et traitement de * pour la borne sup :
124 // -- on autorise uniquement les ecritures 3.0.*, 3.*
125 $minimum = $regs[1];
126 $maximum = $regs[2];
127
128 // si une borne de compatibilité supérieure a été définie (dans
129 // mes_options.php, sous la forme : define('_DEV_PLUGINS', '3.1.99');
130 // on l'utilise (phase de dev, de test...) mais *que* en cas de comparaison
131 // avec la version de SPIP (ne nuit donc pas aux tests de necessite
132 // entre plugins)
133 if (defined('_DEV_PLUGINS') && $avec_quoi == 'spip') {
134 $maximum = _DEV_PLUGINS.']';
135 }
136
137 $minimum_inc = $intervalle{0}=="[";
138 $maximum_inc = substr($intervalle,-1)=="]";
139
140 if (strlen($minimum)){
141 if ($minimum_inc AND spip_version_compare($version,$minimum,'<')) return false;
142 if (!$minimum_inc AND spip_version_compare($version,$minimum,'<=')) return false;
143 }
144 if (strlen($maximum)){
145 if ($maximum_inc AND spip_version_compare($version,$maximum,'>')) return false;
146 if (!$maximum_inc AND spip_version_compare($version,$maximum,'>=')) return false;
147 }
148 return true;
149 }
150
151
152
153 // Construire la liste des infos strictement necessaires aux plugins a activer
154 // afin de les memoriser dans une meta pas trop grosse
155 // http://doc.spip.org/@liste_plugin_valides
156 function liste_plugin_valides($liste_plug, $force = false)
157 {
158 $liste_ext = liste_plugin_files(_DIR_PLUGINS_DIST);
159 $get_infos = charger_fonction('get_infos','plugins');
160 $infos = array(
161 // lister les extensions qui sont automatiquement actives
162 '_DIR_PLUGINS_DIST' => $get_infos($liste_ext, $force, _DIR_PLUGINS_DIST),
163 '_DIR_PLUGINS' => $get_infos($liste_plug, $force, _DIR_PLUGINS)
164 );
165
166 // creer une premiere liste non ordonnee mais qui ne retient
167 // que les plugins valides, et dans leur derniere version en cas de doublon
168 $infos['_DIR_RESTREINT'][''] = $get_infos('./',$force,_DIR_RESTREINT);
169 $infos['_DIR_RESTREINT']['SPIP']['version'] = $GLOBALS['spip_version_branche'];
170 $infos['_DIR_RESTREINT']['SPIP']['chemin'] = array();
171 $liste_non_classee = array('SPIP'=>array(
172 'nom' => 'SPIP',
173 'etat' => 'stable',
174 'version' => $GLOBALS['spip_version_branche'],
175 'dir_type' => '_DIR_RESTREINT',
176 'dir'=> '',
177 )
178 );
179
180 foreach($liste_ext as $plug){
181 if (isset($infos['_DIR_PLUGINS_DIST'][$plug]))
182 plugin_valide_resume($liste_non_classee, $plug, $infos, '_DIR_PLUGINS_DIST');
183 }
184 foreach($liste_plug as $plug) {
185 if (isset($infos['_DIR_PLUGINS'][$plug]))
186 plugin_valide_resume($liste_non_classee, $plug, $infos, '_DIR_PLUGINS');
187 }
188
189 if (defined('_DIR_PLUGINS_SUPPL') and _DIR_PLUGINS_SUPPL) {
190 $infos['_DIR_PLUGINS_SUPPL'] = $get_infos($liste_plug, false, _DIR_PLUGINS_SUPPL);
191 foreach($liste_plug as $plug) {
192 if (isset($infos['_DIR_PLUGINS_SUPPL'][$plug]))
193 plugin_valide_resume($liste_non_classee, $plug, $infos, '_DIR_PLUGINS_SUPPL');
194 }
195 }
196
197 // les procure de core.xml sont consideres comme des plugins proposes,
198 // mais surchargeables (on peut activer un plugin qui procure ca pour l'ameliorer,
199 // donc avec le meme prefixe)
200 foreach($infos['_DIR_RESTREINT']['']['procure'] as $procure) {
201 $p = strtoupper($procure['nom']);
202 if (!isset($liste_non_classee[$p])){
203 $procure['etat'] = '?';
204 $procure['dir_type'] = '_DIR_RESTREINT';
205 $procure['dir'] = '';
206 $liste_non_classee[$p] = $procure;
207 }
208 }
209
210 return array($infos, $liste_non_classee);
211 }
212
213 // Ne retenir un plugin que s'il est valide
214 // et dans leur plus recente version compatible
215 // avec la version presente de SPIP
216
217 function plugin_valide_resume(&$liste, $plug, $infos, $dir)
218 {
219 $i = $infos[$dir][$plug];
220 if (isset($i['erreur']) AND $i['erreur'])
221 return;
222 if (!plugin_version_compatible($i['compatibilite'], $GLOBALS['spip_version_branche'],'spip'))
223 return;
224 $p = strtoupper($i['prefix']);
225 if (!isset($liste[$p])
226 OR spip_version_compare($i['version'],$liste[$p]['version'],'>')) {
227 $liste[$p] = array(
228 'nom' => $i['nom'],
229 'etat' => $i['etat'],
230 'version'=> $i['version'],
231 'dir'=> $plug,
232 'dir_type' => $dir
233 );
234 }
235 }
236
237 /**
238 * extrait les chemins d'une liste de plugin
239 * selectionne au passage ceux qui sont dans $dir_plugins uniquement
240 * si valeur non vide
241 *
242 * @param array $liste
243 * @param string $dir_plugins
244 * @return array
245 */
246 function liste_chemin_plugin($liste, $dir_plugins=_DIR_PLUGINS){
247 foreach ($liste as $prefix=>$infos) {
248 if (!$dir_plugins
249 OR (
250 defined($infos['dir_type'])
251 AND constant($infos['dir_type'])==$dir_plugins))
252 $liste[$prefix] = $infos['dir'];
253 else
254 unset($liste[$prefix]);
255 }
256 return $liste;
257 }
258
259 /**
260 * Liste les chemins vers les plugins actifs du dossier fourni en argument
261 * a partir d'une liste d'elelements construits par plugin_valide_resume
262 *
263 * @return array
264 */
265 // http://doc.spip.org/@liste_chemin_plugin_actifs
266 function liste_chemin_plugin_actifs($dir_plugins=_DIR_PLUGINS){
267 include_spip('plugins/installer');
268 return liste_chemin_plugin(liste_plugin_actifs(), $dir_plugins);
269 }
270
271 // Pour tester utilise, il faut connaitre tous les plugins
272 // qui seront forcement pas la a la fin,
273 // car absent de la liste des plugins actifs.
274 // Il faut donc construire une liste ordonnee
275 // Cette fonction detecte des dependances circulaires,
276 // avec un doute sur un "utilise" qu'on peut ignorer.
277 // Mais ne pas inserer silencieusement et risquer un bug sournois latent
278
279 function plugin_trier($infos, $liste_non_classee)
280 {
281 $toute_la_liste = $liste_non_classee;
282 $liste = $ordre = array();
283 $count = 0;
284 while ($c=count($liste_non_classee) AND $c!=$count){ // tant qu'il reste des plugins a classer, et qu'on ne stagne pas
285 #echo "tour::";var_dump($liste_non_classee);
286 $count = $c;
287 foreach($liste_non_classee as $p=>$resume) {
288 $plug = $resume['dir'];
289 $dir_type = $resume['dir_type'];
290 $info1 = $infos[$dir_type][$plug];
291 // si des plugins sont necessaires,
292 // on ne peut inserer qu'apres eux
293 foreach($info1['necessite'] as $need){
294 $nom = strtoupper($need['nom']);
295 $compat = isset($need['compatibilite']) ? $need['compatibilite'] : '';
296 if (!isset($liste[$nom]) OR !plugin_version_compatible($compat,$liste[$nom]['version'])) {
297 $info1 = false;
298 break;
299 }
300 }
301 if (!$info1) continue;
302 // idem si des plugins sont utiles,
303 // sauf si ils sont de toute facon absents de la liste
304 foreach($info1['utilise'] as $need){
305 $nom = strtoupper($need['nom']);
306 $compat = isset($need['compatibilite']) ? $need['compatibilite'] : '';
307 if (isset($toute_la_liste[$nom])) {
308 if (!isset($liste[$nom]) OR
309 !plugin_version_compatible($compat, $liste[$nom]['version'])) {
310 $info1 = false;
311 break;
312 }
313 }
314 }
315 if ($info1) {
316 $ordre[$p] = $info1;
317 $liste[$p] = $liste_non_classee[$p];
318 unset($liste_non_classee[$p]);
319 }
320 }
321 }
322 return array($liste, $ordre, $liste_non_classee);
323 }
324
325 // Collecte les erreurs dans la meta
326
327 function plugins_erreurs($liste_non_classee, $liste, $infos, $msg=array())
328 {
329 static $erreurs = array();
330 foreach($liste_non_classee as $p=>$resume){
331 $dir_type = $resume['dir_type'];
332 $plug = $resume['dir'];
333 $k = $infos[$dir_type][$plug];
334 $plug = constant($dir_type) . $plug;
335 if (!isset($msg[$p])) {
336 if (!$msg[$p] = plugin_necessite($k['necessite'], $liste))
337 $msg[$p] = plugin_necessite($k['utilise'], $liste);
338 } else {
339 foreach($msg[$p] as $c => $l)
340 $msg[$p][$c] = plugin_controler_lib($l['nom'], $l['lien']);
341 }
342 $erreurs[$plug] = $msg[$p];
343 }
344 ecrire_meta('plugin_erreur_activation', serialize($erreurs));
345 }
346
347 function plugin_donne_erreurs($raw=false, $raz=true) {
348 if (!isset($GLOBALS['meta']['plugin_erreur_activation'])) return $raw?array():'';
349 $list = @unserialize($GLOBALS['meta']['plugin_erreur_activation']);
350 // Compat ancienne version
351 if (!$list)
352 $list = $raw?array():$GLOBALS['meta']['plugin_erreur_activation'];
353 elseif(!$raw) {
354 foreach($list as $plug => $msg)
355 $list[$plug] = "<li>" . _T('plugin_impossible_activer', array('plugin' => $plug))
356 . "<ul><li>" . implode("</li><li>", $msg) . "</li></ul></li>";
357 $list ="<ul>" . join("\n", $list) . "</ul>";
358 }
359 if ($raz)
360 effacer_meta('plugin_erreur_activation');
361 return $list;
362 }
363
364 /**
365 * Teste des dependances
366 * Et verifie que chaque dependance est presente
367 * dans la liste de plugins donnee
368 *
369 * @param array $n
370 * Tableau de dependances dont on souhaite verifier leur presence
371 * @param array $liste
372 * Tableau des plugins presents
373 * @return array
374 * Tableau des messages d'erreurs recus. Il sera vide si tout va bien.
375 *
376 **/
377 function plugin_necessite($n, $liste) {
378 $msg = array();
379 foreach($n as $need){
380 $id = strtoupper($need['nom']);
381 if ($r = plugin_controler_necessite($liste, $id, $need['compatibilite'])) {
382 $msg[] = $r;
383 }
384 }
385 return $msg;
386 }
387
388 /**
389 * Verifie qu'une dependance (plugin) est bien presente.
390 *
391 * @param $liste
392 * Liste de description des plugins
393 * @param $nom
394 * Le plugin donc on cherche la presence
395 * @param $version
396 * L'éventuelle intervalle de compatibilité de la dependance. ex: [1.1.0;]
397 * @return string.
398 * Vide si ok,
399 * Message d'erreur lorsque la dependance est absente.
400 **/
401 function plugin_controler_necessite($liste, $nom, $version)
402 {
403 if (isset($liste[$nom]) AND plugin_version_compatible($version,$liste[$nom]['version'])) {
404 return '';
405 }
406 // retrouver le minimum
407 if (preg_match(_EXTRAIRE_INTERVALLE, $version, $regs)) {
408 $minimum = $regs[1];
409 if ($minimum) {
410 return _T('plugin_necessite_plugin', array(
411 'plugin' => $nom,
412 'version' => $minimum));
413 }
414 }
415 return _T('plugin_necessite_plugin_sans_version', array('plugin' => $nom));
416 }
417
418 function plugin_controler_lib($lib, $url)
419 {
420 /* Feature sortie du core, voir STP
421 * if ($url) {
422 include_spip('inc/charger_plugin');
423 $url = '<br />' . bouton_telechargement_plugin($url, 'lib');
424 }*/
425 return _T('plugin_necessite_lib', array('lib'=>$lib)) . " <a href='$url'>$url</a>";
426 }
427
428 // Pour compatibilite et lisibilite du code
429 function actualise_plugins_actifs($pipe_recherche = false){
430 return ecrire_plugin_actifs('', $pipe_recherche, 'force');
431 }
432
433 // mise a jour du meta en fonction de l'etat du repertoire
434 // Les ecrire_meta() doivent en principe aussi initialiser la valeur a vide
435 // si elle n'existe pas
436 // risque de pb en php5 a cause du typage ou de null (verifier dans la doc php)
437 // @return true/false si il y a du nouveau
438 // http://doc.spip.org/@ecrire_plugin_actifs
439 function ecrire_plugin_actifs($plugin,$pipe_recherche=false,$operation='raz') {
440
441 // creer le repertoire cache/ si necessaire ! (installation notamment)
442 sous_repertoire(_DIR_CACHE, '', false,true);
443
444 if (!spip_connect()) return false;
445 if ($operation!='raz') {
446 $plugin_valides = liste_chemin_plugin_actifs();
447 $plugin_valides = is_plugin_dir($plugin_valides);
448 if(defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL){
449 $plugin_valides_supp = liste_chemin_plugin_actifs(_DIR_PLUGINS_SUPPL);
450 $plugin_valides_supp = is_plugin_dir($plugin_valides_supp,_DIR_PLUGINS_SUPPL);
451 $plugin_valides = array_merge($plugin_valides,$plugin_valides_supp);
452 }
453 // si des plugins sont en attentes (coches mais impossible a activer)
454 // on les reinjecte ici
455 if (isset($GLOBALS['meta']['plugin_attente'])
456 AND $a = unserialize($GLOBALS['meta']['plugin_attente']))
457 $plugin_valides = $plugin_valides + liste_chemin_plugin($a);
458
459 if ($operation=='ajoute')
460 $plugin = array_merge($plugin_valides,$plugin);
461 elseif ($operation=='enleve')
462 $plugin = array_diff($plugin_valides,$plugin);
463 else $plugin = $plugin_valides;
464 }
465 $actifs_avant = $GLOBALS['meta']['plugin'];
466
467 // si une fonction de gestion de dependances existe, l'appeler ici
468 if ($ajouter_dependances = charger_fonction("ajouter_dependances","plugins",true)){
469 $plugin = $ajouter_dependances($plugin);
470 }
471
472 // recharger le xml des plugins a activer
473 // on forcer le reload ici, meme si le fichier xml n'a pas change
474 // pour ne pas rater l'ajout ou la suppression d'un fichier fonctions/options/administrations
475 // pourra etre evite quand on ne supportera plus les plugin.xml
476 // en deplacant la detection de ces fichiers dans la compilation ci dessous
477 list($infos,$liste) = liste_plugin_valides($plugin,true);
478 // trouver l'ordre d'activation
479 list($plugin_valides,$ordre,$reste) = plugin_trier($infos, $liste);
480 if ($reste) plugins_erreurs($reste, $liste, $infos);
481 // Ignorer les plugins necessitant une lib absente
482 // et preparer la meta d'entete Http
483 $err = $msg = $header = array();
484 foreach($plugin_valides as $p => $resume) {
485 $header[]= $p.($resume['version']?"(".$resume['version'].")":"");
486 if ($resume['dir']){
487 foreach($infos[$resume['dir_type']][$resume['dir']]['lib'] as $l) {
488 if (!find_in_path($l['nom'], 'lib/')) {
489 $err[$p] = $resume;
490 $msg[$p][] = $l;
491 unset($plugin_valides[$p]);
492 }
493 }
494 }
495 }
496 if ($err) plugins_erreurs($err, '', $infos, $msg);
497
498 if (isset($GLOBALS['meta']['message_crash_plugins']))
499 effacer_meta('message_crash_plugins');
500 ecrire_meta('plugin',serialize($plugin_valides));
501 $liste = array_diff_key($liste,$plugin_valides);
502 ecrire_meta('plugin_attente',serialize($liste));
503 $header = strtolower(implode(",",$header));
504 ecrire_meta('plugin_header',substr($header,0,900));
505 if (!isset($GLOBALS['spip_header_silencieux']) OR !$GLOBALS['spip_header_silencieux'])
506 ecrire_fichier(_DIR_VAR."config.txt", (defined('_HEADER_COMPOSED_BY') ? _HEADER_COMPOSED_BY:"Composed-By: SPIP") . ' '. $GLOBALS['spip_version_affichee'] . " @ www.spip.net + " . $header);
507 else
508 @unlink(_DIR_VAR."config.txt");
509 // generer charger_plugins_chemin.php
510 plugins_precompile_chemin($plugin_valides, $ordre);
511 // generer les fichiers
512 // charger_plugins_options.php
513 // charger_plugins_fonctions.php
514 // et retourner les fichiers a verifier
515 plugins_precompile_xxxtions($plugin_valides, $ordre);
516 // mise a jour de la matrice des pipelines
517 pipeline_matrice_precompile($plugin_valides, $ordre, $pipe_recherche);
518 // generer le fichier _CACHE_PIPELINE
519 pipeline_precompile();
520
521 // attendre eventuellement l'invalidation du cache opcode
522 spip_attend_invalidation_opcode_cache();
523
524 if (spip_connect()) {
525 // lancer et initialiser les nouveaux crons !
526 include_spip('inc/genie');
527 genie_queue_watch_dist();
528 }
529
530 return ($GLOBALS['meta']['plugin'] != $actifs_avant);
531 }
532
533 function plugins_precompile_chemin($plugin_valides, $ordre)
534 {
535 $chemins = array();
536 $contenu = "";
537 foreach($ordre as $p => $info){
538 // $ordre peur contenir des plugins en attente et non valides pour ce hit
539 if (isset($plugin_valides[$p])){
540 $dir_type = $plugin_valides[$p]['dir_type'];
541 $plug = $plugin_valides[$p]['dir'];
542 // definir le plugin, donc le path avant l'include du fichier options
543 // permet de faire des include_spip pour attraper un inc_ du plugin
544
545 $dir = $dir_type.".'" . $plug ."/'";
546
547 $prefix = strtoupper(preg_replace(',\W,','_',$info['prefix']));
548 if ($prefix!=="SPIP"){
549 $contenu .= "define('_DIR_PLUGIN_$prefix',$dir);\n";
550 foreach($info['chemin'] as $chemin){
551 if (!isset($chemin['version']) OR plugin_version_compatible($chemin['version'],$GLOBALS['spip_version_branche'],'spip')){
552 $dir = $chemin['path'];
553 if (strlen($dir) AND $dir{0}=="/") $dir = substr($dir,1);
554 if (strlen($dir) AND $dir=="./") $dir = '';
555 if (strlen($dir)) $dir = rtrim($dir,'/').'/';
556 if (!isset($chemin['type']) OR $chemin['type']=='public')
557 $chemins['public'][]="_DIR_PLUGIN_$prefix".(strlen($dir)?".'$dir'":"");
558 if (!isset($chemin['type']) OR $chemin['type']=='prive')
559 $chemins['prive'][]="_DIR_PLUGIN_$prefix".(strlen($dir)?".'$dir'":"");
560 }
561 }
562 }
563 }
564 }
565 if (count($chemins)){
566 $contenu .= "if (_DIR_RESTREINT) _chemin(implode(':',array(".implode(',',array_reverse($chemins['public'])).")));\n"
567 . "else _chemin(implode(':',array(".implode(',',array_reverse($chemins['prive'])).")));\n";
568 }
569
570 ecrire_fichier_php(_CACHE_PLUGINS_PATH, $contenu);
571 }
572
573 function plugins_precompile_xxxtions($plugin_valides, $ordre)
574 {
575 $contenu = array('options' => '', 'fonctions' =>'');
576 $boutons = array();
577 $onglets = array();
578 $sign = "";
579
580 foreach($ordre as $p => $info){
581 // $ordre peur contenir des plugins en attente et non valides pour ce hit
582 if (isset($plugin_valides[$p])){
583 $dir_type = $plugin_valides[$p]['dir_type'];
584 $plug = $plugin_valides[$p]['dir'];
585 $dir = constant($dir_type);
586 $root_dir_type = str_replace('_DIR_','_ROOT_',$dir_type);
587 if ($info['menu'])
588 $boutons = array_merge($boutons,$info['menu']);
589 if ($info['onglet'])
590 $onglets = array_merge($onglets,$info['onglet']);
591 foreach($contenu as $charge => $v){
592 // si pas declare/detecte a la lecture du paquet.xml,
593 // detecer a nouveau ici puisque son ajout ne provoque pas une modif du paquet.xml
594 // donc ni sa relecture, ni sa detection
595 if (!isset($info[$charge])
596 AND $dir // exclure le cas du plugin "SPIP"
597 AND file_exists("$dir$plug/paquet.xml") // uniquement pour les paquet.xml
598 ){
599 if (is_readable("$dir$plug/".($file=$info['prefix']."_".$charge.".php"))){
600 $info[$charge] = array($file);
601 }
602 }
603 if (isset($info[$charge])){
604 $files = $info[$charge];
605 foreach($files as $k=>$file){
606 // on genere un if file_exists devant chaque include
607 // pour pouvoir garder le meme niveau d'erreur general
608 $file = trim($file);
609 if (!is_readable("$dir$plug/$file")
610 // uniquement pour les paquet.xml
611 AND file_exists("$dir$plug/paquet.xml")){
612 unset($info[$charge][$k]);
613 }
614 else {
615 $_file = $root_dir_type . ".'$plug/$file'";
616 $contenu[$charge] .= "include_once_check($_file);\n";
617 }
618 }
619 }
620 }
621 $sign .= md5(serialize($info));
622 }
623 }
624
625 $contenu['options'] = "define('_PLUGINS_HASH','".md5($sign)."');\n" . $contenu['options'];
626 $contenu['fonctions'] .= plugin_ongletbouton("boutons_plugins", $boutons)
627 . plugin_ongletbouton("onglets_plugins", $onglets);
628
629 ecrire_fichier_php(_CACHE_PLUGINS_OPT, $contenu['options']);
630 ecrire_fichier_php(_CACHE_PLUGINS_FCT, $contenu['fonctions']);
631 }
632
633 function plugin_ongletbouton($nom, $val)
634 {
635 if (!$val) $val = array();
636 define("_UPDATED_$nom",$val = serialize($val));
637 define("_UPDATED_md5_$nom",$md5=md5($val));
638 $val = "unserialize('".str_replace("'","\'",$val)."')";
639 return
640 "if (!function_exists('$nom')) {\n"
641 ."function $nom(){return defined('_UPDATED_$nom')?unserialize(_UPDATED_$nom):$val;}\n"
642 ."function md5_$nom(){return defined('_UPDATED_md5_$nom')?_UPDATED_md5_$nom:'".$md5."';}\n"
643 ."}\n";
644 }
645
646 // creer le fichier CACHE_PLUGIN_VERIF a partir de
647 // $GLOBALS['spip_pipeline']
648 // $GLOBALS['spip_matrice']
649
650 function pipeline_matrice_precompile($plugin_valides, $ordre, $pipe_recherche)
651 {
652 static $liste_pipe_manquants=array();
653 if (($pipe_recherche)&&(!in_array($pipe_recherche,$liste_pipe_manquants)))
654 $liste_pipe_manquants[]=$pipe_recherche;
655
656 foreach($ordre as $p => $info){
657 // $ordre peur contenir des plugins en attente et non valides pour ce hit
658 if (isset($plugin_valides[$p])){
659 $dir_type = $plugin_valides[$p]['dir_type'];
660 $root_dir_type = str_replace('_DIR_','_ROOT_',$dir_type);
661 $plug = $plugin_valides[$p]['dir'];
662 $prefix = (($info['prefix']=="spip")?"":$info['prefix']."_");
663 if (isset($info['pipeline']) AND is_array($info['pipeline'])){
664 foreach($info['pipeline'] as $pipe){
665 $nom = $pipe['nom'];
666 if (isset($pipe['action']))
667 $action = $pipe['action'];
668 else
669 $action = $nom;
670 $nomlower = strtolower($nom);
671 if ($nomlower!=$nom
672 AND isset($GLOBALS['spip_pipeline'][$nom])
673 AND !isset($GLOBALS['spip_pipeline'][$nomlower])){
674 $GLOBALS['spip_pipeline'][$nomlower] = $GLOBALS['spip_pipeline'][$nom];
675 unset($GLOBALS['spip_pipeline'][$nom]);
676 }
677 $nom = $nomlower;
678 // une action vide est une declaration qui ne doit pas etre compilee !
679 if (!isset($GLOBALS['spip_pipeline'][$nom])) // creer le pipeline eventuel
680 $GLOBALS['spip_pipeline'][$nom]="";
681 if ($action){
682 if (strpos($GLOBALS['spip_pipeline'][$nom],"|$prefix$action")===FALSE)
683 $GLOBALS['spip_pipeline'][$nom] = preg_replace(",(\|\||$),","|$prefix$action\\1",$GLOBALS['spip_pipeline'][$nom],1);
684 if (isset($pipe['inclure'])){
685 $GLOBALS['spip_matrice']["$prefix$action"] =
686 "$root_dir_type:$plug/".$pipe['inclure'];
687 }
688 }
689 }
690 }
691 }
692 }
693
694 // on charge les fichiers d'options qui peuvent completer
695 // la globale spip_pipeline egalement
696 if (@is_readable(_CACHE_PLUGINS_PATH))
697 include_once(_CACHE_PLUGINS_PATH); // securite : a priori n'a pu etre fait plus tot
698 if (@is_readable(_CACHE_PLUGINS_OPT)) {
699 include_once(_CACHE_PLUGINS_OPT);
700 } else {
701 spip_log("pipelines desactives: impossible de produire " . _CACHE_PLUGINS_OPT);
702 }
703
704 // on ajoute les pipe qui ont ete recenses manquants
705 foreach($liste_pipe_manquants as $add_pipe)
706 if (!isset($GLOBALS['spip_pipeline'][$add_pipe]))
707 $GLOBALS['spip_pipeline'][$add_pipe]= '';
708 }
709
710 // precompilation des pipelines
711 // http://doc.spip.org/@pipeline_precompile
712 function pipeline_precompile(){
713 global $spip_pipeline, $spip_matrice;
714
715 $content = "";
716 foreach($spip_pipeline as $action=>$pipeline){
717 $s_inc = "";
718 $s_call = "";
719 $pipe = array_filter(explode('|',$pipeline));
720 // Eclater le pipeline en filtres et appliquer chaque filtre
721 foreach ($pipe as $fonc) {
722 $fonc = trim($fonc);
723 $s_call .= '$val = minipipe(\''.$fonc.'\', $val);'."\n";
724 if (isset($spip_matrice[$fonc])){
725 $file = $spip_matrice[$fonc];
726 $file = "'$file'";
727 // si un _DIR_XXX: est dans la chaine, on extrait la constante
728 if (preg_match(",(_(DIR|ROOT)_[A-Z_]+):,Ums",$file,$regs)){
729 $dir = $regs[1];
730 $root_dir = str_replace('_DIR_','_ROOT_',$dir);
731 if (defined($root_dir))
732 $dir = $root_dir;
733 $file = str_replace($regs[0],"'.".$dir.".'",$file);
734 $file = str_replace("''.","",$file);
735 $file = str_replace(constant($dir), '', $file);
736 }
737 $s_inc .= "include_once_check($file);\n";
738 }
739 }
740 if (strlen($s_inc))
741 $s_inc = "static \$inc=null;\nif (!\$inc){\n$s_inc\$inc=true;\n}\n";
742 $content .= "// Pipeline $action \n"
743 . "function execute_pipeline_$action(&\$val){\n"
744 . $s_inc
745 . $s_call
746 . "return \$val;\n}\n";
747 }
748 ecrire_fichier_php(_CACHE_PIPELINES, $content);
749 clear_path_cache();
750 }
751
752
753 // http://doc.spip.org/@plugin_est_installe
754 function plugin_est_installe($plug_path){
755 $plugin_installes = isset($GLOBALS['meta']['plugin_installes'])?unserialize($GLOBALS['meta']['plugin_installes']):array();
756 if (!$plugin_installes) return false;
757 return in_array($plug_path,$plugin_installes);
758 }
759
760
761 function plugin_installes_meta()
762 {
763 $installer_plugins = charger_fonction('installer', 'plugins');
764 $meta_plug_installes = array();
765 foreach (unserialize($GLOBALS['meta']['plugin']) as $prefix=>$resume) {
766 if ($plug = $resume['dir']){
767 $infos = $installer_plugins($plug, 'install', $resume['dir_type']);
768 if ($infos){
769 if (!is_array($infos) OR $infos['install_test'][0])
770 $meta_plug_installes[] = $plug;
771 if (is_array($infos)){
772 list($ok, $trace) = $infos['install_test'];
773 include_spip('inc/filtres_boites');
774 echo "<div class='install-plugins svp_retour'>"
775 .boite_ouvrir(_T('plugin_titre_installation', array('plugin' => typo($infos['nom']))), ($ok ? 'success' : 'error'))
776 .$trace
777 ."<div class='result'>"
778 .($ok ? ((isset($infos['upgrade']) && $infos['upgrade']) ? _T("plugin_info_upgrade_ok") : _T("plugin_info_install_ok")) : _T("avis_operation_echec"))
779 ."</div>"
780 .boite_fermer()
781 ."</div>";
782 }
783 }
784 }
785 }
786 ecrire_meta('plugin_installes',serialize($meta_plug_installes),'non');
787 }
788
789 function ecrire_fichier_php($nom, $contenu, $comment='')
790 {
791 ecrire_fichier($nom,
792 '<'.'?php' . "\n" . $comment ."\nif (defined('_ECRIRE_INC_VERSION')) {\n". $contenu . "}\n?".'>');
793 }
794