Completed
Push — master ( 95390d...9ad9dd )
by cam
09:15
created

plugin.php ➔ plugins_precompile_chemin()   F

Complexity

Conditions 20
Paths 154

Size

Total Lines 52

Duplication

Lines 7
Ratio 13.46 %

Importance

Changes 0
Metric Value
cc 20
nc 154
nop 2
dl 7
loc 52
rs 3.7166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2018                                                *
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
/**
14
 * Gestion de l'activation des plugins
15
 *
16
 * @package SPIP\Core\Plugins
17
 **/
18
19
if (!defined('_ECRIRE_INC_VERSION')) {
20
	return;
21
}
22
23
/** l'adresse du repertoire de telechargement et de decompactage des plugins */
24
define('_DIR_PLUGINS_AUTO', _DIR_PLUGINS . 'auto/');
25
26
#include_spip('inc/texte'); // ????? Appelle public/parametrer trop tot avant la reconstruction du chemin des plugins.
27
include_spip('plugins/installer');
28
29
/**
30
 * Retourne la description de chaque plugin présent dans un répertoire 
31
 *
32
 * Lecture des sous repertoire plugin existants
33
 * 
34
 * @example
35
 *     - `liste_plugin_files()`
36
 *     - `liste_plugin_files(_DIR_PLUGINS_DIST)`
37
 *     - `liste_plugin_files(_DIR_PLUGINS_SUPPL)`
38
 * 
39
 * @uses fast_find_plugin_dirs()
40
 * @uses plugins_get_infos_dist()
41
 * 
42
 * @param string|null $dir_plugins
43
 *     - string : Chemin (relatif à la racine du site) du répertoire à analyser. 
44
 *     - null : utilise le chemin `_DIR_PLUGINS`.
45
 * @return array
46
**/
47
function liste_plugin_files($dir_plugins = null) {
48
	static $plugin_files = array();
49
	if (is_null($dir_plugins)) {
50
		$dir_plugins = _DIR_PLUGINS;
51
	}
52
	if (!isset($plugin_files[$dir_plugins])
53
		or count($plugin_files[$dir_plugins]) == 0
54
	) {
55
		$plugin_files[$dir_plugins] = array();
56
		foreach (fast_find_plugin_dirs($dir_plugins) as $plugin) {
57
			$plugin_files[$dir_plugins][] = substr($plugin, strlen($dir_plugins));
58
		}
59
60
		sort($plugin_files[$dir_plugins]);
61
		// et on lit le XML de tous les plugins pour le mettre en cache
62
		// et en profiter pour nettoyer ceux qui n'existent plus du cache
63
		$get_infos = charger_fonction('get_infos', 'plugins');
64
		$get_infos($plugin_files[$dir_plugins], false, $dir_plugins, true);
65
	}
66
67
	return $plugin_files[$dir_plugins];
68
}
69
70
/**
71
 * Recherche rapide des répertoires de plugins contenus dans un répertoire
72
 *
73
 * @uses is_plugin_dir()
74
 * 
75
 * @param string $dir
76
 *     Chemin du répertoire dont on souhaite retourner les sous répertoires
77
 * @param int $max_prof
78
 *     Profondeur maximale des sous répertoires
79
 * @return array
80
 *     Liste complète des répeertoires
81
**/
82
function fast_find_plugin_dirs($dir, $max_prof = 100) {
83
	$fichiers = array();
84
	// revenir au repertoire racine si on a recu dossier/truc
85
	// pour regarder dossier/truc/ ne pas oublier le / final
86
	$dir = preg_replace(',/[^/]*$,', '', $dir);
87
	if ($dir == '') {
88
		$dir = '.';
89
	}
90
91
	if (!is_dir($dir)) {
92
		return $fichiers;
93
	}
94
	if (is_plugin_dir($dir, '')) {
95
		$fichiers[] = $dir;
96
97
		return $fichiers;
98
	}
99
	if ($max_prof <= 0) {
100
		return $fichiers;
101
	}
102
103
	$subdirs = array();
104
	if (@is_dir($dir) and is_readable($dir) and $d = opendir($dir)) {
105 View Code Duplication
		while (($f = readdir($d)) !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
106
			if ($f[0] != '.' # ignorer . .. .svn etc
107
				and $f != 'CVS'
108
				and is_dir($f = "$dir/$f")
109
			) {
110
				$subdirs[] = $f;
111
			}
112
		}
113
		closedir($d);
114
	}
115
116
	foreach ($subdirs as $d) {
117
		$fichiers = array_merge($fichiers, fast_find_plugin_dirs("$d/", $max_prof - 1));
118
	}
119
120
	return $fichiers;
121
}
122
123
/**
124
 * Indique si un répertoire (ou plusieurs) est la racine d'un plugin SPIP
125
 *
126
 * Vérifie le ou les chemins relatifs transmis pour vérifier qu'ils contiennent
127
 * un `plugin.xml` ou un `paquet.xml`. Les chemins valides sont retournés.
128
 * 
129
 * @param string|string[] $dir
130
 *     Chemin (relatif à `$dir_plugins`), ou liste de chemins à tester
131
 * @param string|null $dir_plugins
132
 *     - string : Chemin de répertoire (relatif à la `_DIR_RACINE`), départ des chemin(s) à tester
133
 *     - null (par défaut) : utilise le chemin `_DIR_PLUGINS`
134
 * @return string|string[]
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
135
 *     - string : Le chemin accepté (c'était un plugin)
136
 *     - '' : ce n'était pas un chemin valide
137
 *     - array : Ensemble des chemins acceptés (si `$dir` était array)
138
**/
139
function is_plugin_dir($dir, $dir_plugins = null) {
140
141
	if (is_array($dir)) {
142
		foreach ($dir as $k => $d) {
143
			if (!is_plugin_dir($d, $dir_plugins)) {
144
				unset($dir[$k]);
145
			}
146
		}
147
148
		return $dir;
149
	}
150
	if (is_null($dir_plugins)) {
151
		$dir_plugins = _DIR_PLUGINS;
152
	}
153
	$search = array("$dir_plugins$dir/plugin.xml", "$dir_plugins$dir/paquet.xml");
154
155
	foreach ($search as $s) {
156
		if (file_exists($s)) {
157
			return $dir;
158
		}
159
	}
160
161
	return '';
162
}
163
164
/** Regexp d'extraction des informations d'un intervalle de compatibilité */
165
define('_EXTRAIRE_INTERVALLE', ',^[\[\(\]]([0-9.a-zRC\s\-]*)[;]([0-9.a-zRC\s\-\*]*)[\]\)\[]$,');
166
167
/**
168
 * Teste si le numéro de version d'un plugin est dans un intervalle donné.
169
 *
170
 * Cette fonction peut être volontairement trompée (phase de développement) :
171
 * voir commentaire infra sur l'utilisation de la constante _DEV_VERSION_SPIP_COMPAT
172
 *
173
 * @uses spip_version_compare()
174
 * 
175
 * @param string $intervalle
176
 *    Un intervalle entre 2 versions. ex: [2.0.0-dev;2.1.*]
177
 * @param string $version
178
 *    Un numéro de version. ex: 3.1.99]
179
 * @param string $avec_quoi
180
 *    Ce avec quoi est testée la compatibilité. par défaut ('')
181
 *    avec un plugin (cas des 'necessite'), parfois ('spip')
182
 *    avec SPIP.
183
 * @return bool
184
 *    True si dans l'intervalle, false sinon.
185
 **/
186
function plugin_version_compatible($intervalle, $version, $avec_quoi = '') {
187
188
	if (!strlen($intervalle)) {
189
		return true;
190
	}
191
	if (!preg_match(_EXTRAIRE_INTERVALLE, $intervalle, $regs)) {
192
		return false;
193
	}
194
	// Extraction des bornes et traitement de * pour la borne sup :
195
	// -- on autorise uniquement les ecritures 3.0.*, 3.*
196
	$minimum = $regs[1];
197
	$maximum = $regs[2];
198
199
	//  si une version SPIP de compatibilité a été définie (dans
200
	//  mes_options.php, sous la forme : define('_DEV_VERSION_SPIP_COMPAT', '3.1.0');
201
	//  on l'utilise (phase de dev, de test...) mais *que* en cas de comparaison
202
	//  avec la version de SPIP (ne nuit donc pas aux tests de necessite
203
	//  entre plugins)
204
	if (defined('_DEV_VERSION_SPIP_COMPAT') and $avec_quoi == 'spip' and $version !== _DEV_VERSION_SPIP_COMPAT) {
205
		if (plugin_version_compatible($intervalle, _DEV_VERSION_SPIP_COMPAT, $avec_quoi)) {
206
			return true;
207
		}
208
		// si pas de compatibilite avec _DEV_VERSION_SPIP_COMPAT, on essaye quand meme avec la vrai version
209
		// cas du plugin qui n'est compatible qu'avec cette nouvelle version
210
	}
211
212
	$minimum_inc = $intervalle{0} == "[";
213
	$maximum_inc = substr($intervalle, -1) == "]";
214
215 View Code Duplication
	if (strlen($minimum)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
216
		if ($minimum_inc and spip_version_compare($version, $minimum, '<')) {
217
			return false;
218
		}
219
		if (!$minimum_inc and spip_version_compare($version, $minimum, '<=')) {
220
			return false;
221
		}
222
	}
223 View Code Duplication
	if (strlen($maximum)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
224
		if ($maximum_inc and spip_version_compare($version, $maximum, '>')) {
225
			return false;
226
		}
227
		if (!$maximum_inc and spip_version_compare($version, $maximum, '>=')) {
228
			return false;
229
		}
230
	}
231
232
	return true;
233
}
234
235
/**
236
 * Construire la liste des infos strictement necessaires aux plugins à activer
237
 * afin de les mémoriser dans une meta pas trop grosse
238
 *
239
 * @uses liste_plugin_files()
240
 * @uses plugins_get_infos_dist()
241
 * @uses plugin_valide_resume()
242
 * @uses plugin_fixer_procure()
243
 * 
244
 * @param array $liste_plug
245
 * @param bool $force
246
 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array[].

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
247
 */
248
function liste_plugin_valides($liste_plug, $force = false) {
249
	$liste_ext = liste_plugin_files(_DIR_PLUGINS_DIST);
250
	$get_infos = charger_fonction('get_infos', 'plugins');
251
	$infos = array(
252
		// lister les extensions qui sont automatiquement actives
253
		'_DIR_PLUGINS_DIST' => $get_infos($liste_ext, $force, _DIR_PLUGINS_DIST),
254
		'_DIR_PLUGINS' => $get_infos($liste_plug, $force, _DIR_PLUGINS)
255
	);
256
257
	// creer une premiere liste non ordonnee mais qui ne retient
258
	// que les plugins valides, et dans leur derniere version en cas de doublon
259
	$infos['_DIR_RESTREINT'][''] = $get_infos('./', $force, _DIR_RESTREINT);
260
	$infos['_DIR_RESTREINT']['SPIP']['version'] = $GLOBALS['spip_version_branche'];
261
	$infos['_DIR_RESTREINT']['SPIP']['chemin'] = array();
262
	$liste_non_classee = array(
263
		'SPIP' => array(
264
			'nom' => 'SPIP',
265
			'etat' => 'stable',
266
			'version' => $GLOBALS['spip_version_branche'],
267
			'dir_type' => '_DIR_RESTREINT',
268
			'dir' => '',
269
		)
270
	);
271
272
	foreach ($liste_ext as $plug) {
273
		if (isset($infos['_DIR_PLUGINS_DIST'][$plug])) {
274
			plugin_valide_resume($liste_non_classee, $plug, $infos, '_DIR_PLUGINS_DIST');
275
		}
276
	}
277 View Code Duplication
	foreach ($liste_plug as $plug) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
278
		if (isset($infos['_DIR_PLUGINS'][$plug])) {
279
			plugin_valide_resume($liste_non_classee, $plug, $infos, '_DIR_PLUGINS');
280
		}
281
	}
282
283
	if (defined('_DIR_PLUGINS_SUPPL') and _DIR_PLUGINS_SUPPL) {
284
		$infos['_DIR_PLUGINS_SUPPL'] = $get_infos($liste_plug, false, _DIR_PLUGINS_SUPPL);
285 View Code Duplication
		foreach ($liste_plug as $plug) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
286
			if (isset($infos['_DIR_PLUGINS_SUPPL'][$plug])) {
287
				plugin_valide_resume($liste_non_classee, $plug, $infos, '_DIR_PLUGINS_SUPPL');
288
			}
289
		}
290
	}
291
292
	plugin_fixer_procure($liste_non_classee, $infos);
293
294
	return array($infos, $liste_non_classee);
295
}
296
297
/**
298
 * Ne retenir un plugin que s'il est valide
299
 * et dans leur plus recente version compatible
300
 * avec la version presente de SPIP
301
 *
302
 * @uses plugin_version_compatible()
303
 * @uses spip_version_compare()
304
 * 
305
 * @param array $liste
306
 * @param string $plug
307
 * @param array $infos
308
 * @param string $dir_type
309
 */
310
function plugin_valide_resume(&$liste, $plug, $infos, $dir_type) {
311
	$i = $infos[$dir_type][$plug];
312
	if (isset($i['erreur']) and $i['erreur']) {
313
		return;
314
	}
315
	if (!plugin_version_compatible($i['compatibilite'], $GLOBALS['spip_version_branche'], 'spip')) {
316
		return;
317
	}
318
	$p = strtoupper($i['prefix']);
319
	if (!isset($liste[$p])
320
		or spip_version_compare($i['version'], $liste[$p]['version'], '>')
321
	) {
322
		$liste[$p] = array(
323
			'nom' => $i['nom'],
324
			'etat' => $i['etat'],
325
			'version' => $i['version'],
326
			'dir' => $plug,
327
			'dir_type' => $dir_type
328
		);
329
	}
330
}
331
332
/**
333
 * Compléter la liste des plugins avec les éventuels procure
334
 *
335
 * les balises `<procure>` sont considerées comme des plugins proposés,
336
 * mais surchargeables (on peut activer un plugin qui procure ça pour l'améliorer,
337
 * donc avec le même prefixe, qui sera pris en compte si il a une version plus grande)
338
 *
339
 * @uses spip_version_compare()
340
 * 
341
 * @param array $liste
342
 * @param array $infos
343
 */
344
function plugin_fixer_procure(&$liste, &$infos) {
345
	foreach ($liste as $p => $resume) {
346
		$i = $infos[$resume['dir_type']][$resume['dir']];
347
		if (isset($i['procure']) and $i['procure']) {
348
			foreach ($i['procure'] as $procure) {
349
				$p = strtoupper($procure['nom']);
350
				$dir = $resume['dir'];
351
				if ($dir) {
352
					$dir .= "/";
353
				}
354
				$dir .= "procure:" . $procure['nom'];
355
356
				$procure['etat'] = '?';
357
				$procure['dir_type'] = $resume['dir_type'];
358
				$procure['dir'] = $dir;
359
360
				// si ce plugin n'est pas deja procure, ou dans une version plus ancienne
361
				// on ajoute cette version a la liste
362
				if (!isset($liste[$p])
363
					or spip_version_compare($procure['version'], $liste[$p]['version'], '>')
364
				) {
365
					$liste[$p] = $procure;
366
367
					// on fournit une information minimale pour ne pas perturber la compilation
368
					$infos[$resume['dir_type']][$dir] = array(
369
						'prefix' => $procure['nom'],
370
						'nom' => $procure['nom'],
371
						'etat' => $procure['etat'],
372
						'version' => $procure['version'],
373
						'chemin' => array(),
374
						'necessite' => array(),
375
						'utilise' => array(),
376
						'lib' => array(),
377
						'menu' => array(),
378
						'onglet' => array(),
379
						'procure' => array(),
380
					);
381
				}
382
			}
383
		}
384
	}
385
}
386
387
/**
388
 * Extrait les chemins d'une liste de plugin
389
 * 
390
 * Sélectionne au passage ceux qui sont dans `$dir_plugins` uniquement
391
 * si valeur non vide
392
 *
393
 * @param array $liste
394
 * @param string $dir_plugins
395
 * @return array
396
 */
397
function liste_chemin_plugin($liste, $dir_plugins = _DIR_PLUGINS) {
398
	foreach ($liste as $prefix => $infos) {
399
		if (!$dir_plugins
400
			or (
401
				defined($infos['dir_type'])
402
				and constant($infos['dir_type']) == $dir_plugins)
403
		) {
404
			$liste[$prefix] = $infos['dir'];
405
		} else {
406
			unset($liste[$prefix]);
407
		}
408
	}
409
410
	return $liste;
411
}
412
413
/**
414
 * Liste les chemins vers les plugins actifs du dossier fourni en argument
415
 * a partir d'une liste d'elelements construits par plugin_valide_resume
416
 *
417
 * @uses liste_plugin_actifs()
418
 * @uses liste_chemin_plugin()
419
 * 
420
 * @param string $dir_plugins
421
 *     Chemin du répertoire de plugins
422
 * @return array
423
 */
424
function liste_chemin_plugin_actifs($dir_plugins = _DIR_PLUGINS) {
425
	include_spip('plugins/installer');
426
427
	return liste_chemin_plugin(liste_plugin_actifs(), $dir_plugins);
428
}
429
430
/**
431
 * Trier les plugins en vériant leur dépendances (qui doivent être présentes)
432
 *
433
 * Pour tester "utilise", il faut connaître tous les plugins
434
 * qui seront forcément absents à la fin,
435
 * car absent de la liste des plugins actifs.
436
 * 
437
 * Il faut donc construire une liste ordonnée.
438
 * 
439
 * Cette fonction détecte des dépendances circulaires,
440
 * avec un doute sur un "utilise" qu'on peut ignorer.
441
 * Mais ne pas insérer silencieusement et risquer un bug sournois latent
442
 * 
443
 * @uses plugin_version_compatible()
444
 * 
445
 * @param array $infos 
446
 *     Répertoire (plugins, plugins-dist, ...) => Couples (prefixes => infos completes) des plugins qu'ils contiennent
447
 * @param array $liste_non_classee
448
 *     Couples (prefixe => description) des plugins qu'on souhaite utiliser
449
 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array[].

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
450
 *     Tableau de 3 éléments :
451
 *     - $liste : couples (prefixes => description) des plugins valides
452
 *     - $ordre : couples (prefixes => infos completes) des plugins triés
453
 *                (les plugins nécessités avant les plugins qui les utilisent)
454
 *     - $liste_non_classee : couples (prefixes => description) des plugins 
455
 *                qui n'ont pas satisfait leurs dépendances
456
**/
457
function plugin_trier($infos, $liste_non_classee) {
458
	$toute_la_liste = $liste_non_classee;
459
	$liste = $ordre = array();
460
	$count = 0;
461
462
	while ($c = count($liste_non_classee) and $c != $count) { // tant qu'il reste des plugins a classer, et qu'on ne stagne pas
463
		#echo "tour::";var_dump($liste_non_classee);
464
		$count = $c;
465
		foreach ($liste_non_classee as $p => $resume) {
466
			$plug = $resume['dir'];
467
			$dir_type = $resume['dir_type'];
468
			$info1 = $infos[$dir_type][$plug];
469
			// si des plugins sont necessaires,
470
			// on ne peut inserer qu'apres eux
471 View Code Duplication
			foreach ($info1['necessite'] as $need) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
472
				$nom = strtoupper($need['nom']);
473
				$compat = isset($need['compatibilite']) ? $need['compatibilite'] : '';
474
				if (!isset($liste[$nom]) or !plugin_version_compatible($compat, $liste[$nom]['version'])) {
475
					$info1 = false;
476
					break;
477
				}
478
			}
479
			if (!$info1) {
480
				continue;
481
			}
482
			// idem si des plugins sont utiles,
483
			// sauf si ils sont de toute facon absents de la liste
484 View Code Duplication
			foreach ($info1['utilise'] as $need) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
485
				$nom = strtoupper($need['nom']);
486
				$compat = isset($need['compatibilite']) ? $need['compatibilite'] : '';
487
				if (isset($toute_la_liste[$nom])) {
488
					if (!isset($liste[$nom]) or
489
						!plugin_version_compatible($compat, $liste[$nom]['version'])
490
					) {
491
						$info1 = false;
492
						break;
493
					}
494
				}
495
			}
496
			if ($info1) {
497
				$ordre[$p] = $info1;
498
				$liste[$p] = $liste_non_classee[$p];
499
				unset($liste_non_classee[$p]);
500
			}
501
		}
502
	}
503
504
	return array($liste, $ordre, $liste_non_classee);
505
}
506
507
/**
508
 * Collecte les erreurs de dépendances des plugins dans la meta `plugin_erreur_activation`
509
 *
510
 * @uses plugin_necessite()
511
 * @uses plugin_controler_lib()
512
 * 
513
 * @param array $liste_non_classee
514
 *     Couples (prefixe => description) des plugins en erreur
515
 * @param array $liste
516
 *     Couples (prefixe => description) des plugins qu'on souhaite utiliser
517
 * @param array $infos 
518
 *     Répertoire (plugins, plugins-dist, ...) => Couples (prefixes => infos completes) des plugins qu'ils contiennent
519
**/
520
function plugins_erreurs($liste_non_classee, $liste, $infos, $msg = array()) {
521
	static $erreurs = array();
522
523
	if (!is_array($liste)) {
524
		$liste = array();
525
	}
526
527
	// les plugins en erreur ne sont pas actifs ; ils ne doivent pas être dans la liste
528
	$liste = array_diff_key($liste, $liste_non_classee);
529
530
	foreach ($liste_non_classee as $p => $resume) {
531
		$dir_type = $resume['dir_type'];
532
		$plug = $resume['dir'];
533
		$k = $infos[$dir_type][$plug];
534
535
		$plug = constant($dir_type) . $plug;
536
		if (!isset($msg[$p])) {
537
			if (!$msg[$p] = plugin_necessite($k['necessite'], $liste, 'necessite')) {
538
				$msg[$p] = plugin_necessite($k['utilise'], $liste, 'utilise');
539
			}
540
		} else {
541
			foreach ($msg[$p] as $c => $l) {
542
				$msg[$p][$c] = plugin_controler_lib($l['nom'], $l['lien']);
543
			}
544
		}
545
		$erreurs[$plug] = $msg[$p];
546
	}
547
548
	ecrire_meta('plugin_erreur_activation', serialize($erreurs));
549
}
550
551
/**
552
 * Retourne les erreurs d'activation des plugins, au format html ou brut
553
 *
554
 * @param bool $raw
555
 *     - true : pour obtenir le tableau brut des erreurs
556
 *     - false : Code HTML
557
 * @param bool $raz
558
 *     - true pour effacer la meta qui stocke les erreurs.
559
 * @return string|array
560
 *     - Liste des erreurs ou code HTML des erreurs
561
**/
562
function plugin_donne_erreurs($raw = false, $raz = true) {
563
	if (!isset($GLOBALS['meta']['plugin_erreur_activation'])) {
564
		return $raw ? array() : '';
565
	}
566
	$list = @unserialize($GLOBALS['meta']['plugin_erreur_activation']);
567
	// Compat ancienne version
568
	if (!$list) {
569
		$list = $raw ? array() : $GLOBALS['meta']['plugin_erreur_activation'];
570
	} elseif (!$raw) {
571
		foreach ($list as $plug => $msg) {
572
			$list[$plug] = "<li>" . _T('plugin_impossible_activer', array('plugin' => $plug))
573
				. "<ul><li>" . implode("</li><li>", $msg) . "</li></ul></li>";
574
		}
575
		$list = "<ul>" . join("\n", $list) . "</ul>";
576
	}
577
	if ($raz) {
578
		effacer_meta('plugin_erreur_activation');
579
	}
580
581
	return $list;
582
}
583
584
/**
585
 * Teste des dépendances
586
 * 
587
 * Et vérifie que chaque dépendance est présente
588
 * dans la liste de plugins donnée
589
 *
590
 * @uses plugin_controler_necessite()
591
 * 
592
 * @param array $n
593
 *    Tableau de dépendances dont on souhaite vérifier leur présence
594
 * @param array $liste
595
 *    Tableau des plugins présents
596
 * @return array
597
 *    Tableau des messages d'erreurs reçus. Il sera vide si tout va bien.
598
 *
599
 **/
600
function plugin_necessite($n, $liste, $balise = 'necessite') {
601
	$msg = array();
602
	foreach ($n as $need) {
603
		$id = strtoupper($need['nom']);
604
		$r = plugin_controler_necessite(
605
			$liste, 
606
			$id, 
607
			isset($need['compatibilite']) ? $need['compatibilite'] : '', 
608
			$balise
609
		);
610
		if ($r) {
611
			$msg[] = $r;
612
		}
613
	}
614
615
	return $msg;
616
}
617
618
/**
619
 * Vérifie qu'une dépendance (plugin) est bien présente.
620
 *
621
 * @uses plugin_version_compatible()
622
 * @uses plugin_message_incompatibilite()
623
 * 
624
 * @param $liste
625
 *    Liste de description des plugins
626
 * @param $nom
627
 *    Le plugin donc on cherche la presence
628
 * @param $intervalle
629
 *    L'éventuelle intervalle de compatibilité de la dépendance. ex: [1.1.0;]
630
 * @param $balise
631
 *    Permet de définir si on teste un utilise ou un nécessite
632
 * @return string.
0 ignored issues
show
Documentation introduced by
The doc-type string. could not be parsed: Unknown type name "string." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
633
 *    Vide si ok,
634
 *    Message d'erreur lorsque la dépendance est absente.
635
 **/
636
function plugin_controler_necessite($liste, $nom, $intervalle, $balise) {
637
	if (isset($liste[$nom]) and plugin_version_compatible($intervalle, $liste[$nom]['version'])) {
638
		return '';
639
	}
640
641
	return plugin_message_incompatibilite(
642
		$intervalle, 
643
		(isset($liste[$nom]) ? $liste[$nom]['version'] : ""), 
644
		$nom, 
645
		$balise
646
	);
647
}
648
649
/**
650
 * @param string $intervalle
651
 *     L'éventuelle intervalle de compatibilité de la dépendance. ex: [1.1.0;]
652
 * @param string $version
653
 *     La version en cours active pour le plugin demandé (ou php ou extension php demandée)
654
 * @param string $nom
655
 *     Le plugin (ou php ou extension php) qui est absent
656
 * @param string $balise
657
 *     Le type de balise utilisé (necessite ou utilise)
658
 * @return string
659
 *     Le message d'erreur.
660
 */
661
function plugin_message_incompatibilite($intervalle, $version, $nom, $balise) {
662
663
	// prendre en compte les erreurs de dépendances à PHP
664
	// ou à une extension PHP avec des messages d'erreurs dédiés.
665
	$type = 'plugin';
666
	if ($nom === 'PHP') {
667
		$type = 'php';
668
	} elseif (strncmp($nom, 'PHP:', 4) === 0) {
669
		$type = 'extension_php';
670
		list(,$nom) = explode(':', $nom, 2);
671
	}
672
673
	if (preg_match(_EXTRAIRE_INTERVALLE, $intervalle, $regs)) {
674
		$minimum = $regs[1];
675
		$maximum = $regs[2];
676
677
		$minimum_inclus = $intervalle{0} == "[";
678
		$maximum_inclus = substr($intervalle, -1) == "]";
679
680 View Code Duplication
		if (strlen($minimum)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
681
			if ($minimum_inclus and spip_version_compare($version, $minimum, '<')) {
682
				return _T("plugin_${balise}_${type}", array(
683
					'plugin' => $nom,
684
					'version' => ' &ge; ' . $minimum
685
				));
686
			}
687
			if (!$minimum_inclus and spip_version_compare($version, $minimum, '<=')) {
688
				return _T("plugin_${balise}_${type}", array(
689
					'plugin' => $nom,
690
					'version' => ' &gt; ' . $minimum
691
				));
692
			}
693
		}
694
695 View Code Duplication
		if (strlen($maximum)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
696
			if ($maximum_inclus and spip_version_compare($version, $maximum, '>')) {
697
				return _T("plugin_${balise}_${type}", array(
698
					'plugin' => $nom,
699
					'version' => ' &le; ' . $maximum
700
				));
701
			}
702
			if (!$maximum_inclus and spip_version_compare($version, $maximum, '>=')) {
703
				return _T("plugin_${balise}_plugin", array(
704
					'plugin' => $nom,
705
					'version' => ' &lt; ' . $maximum
706
				));
707
			}
708
		}
709
	}
710
711
	// note : il ne peut pas y avoir d'erreur sur
712
	// - un 'utilise' sans version.
713
	// - un 'php' sans version.
714
	return _T("plugin_necessite_${type}_sans_version", array('plugin' => $nom));
715
}
716
717
718
function plugin_controler_lib($lib, $url) {
719
	/* Feature sortie du core, voir STP
720
	 * if ($url) {
721
		include_spip('inc/charger_plugin');
722
		$url = '<br />'	. bouton_telechargement_plugin($url, 'lib');
723
	}*/
724
	return _T('plugin_necessite_lib', array('lib' => $lib)) . " <a href='$url'>$url</a>";
725
}
726
727
728
/**
729
 * Calcule la liste des plugins actifs et recompile les fichiers caches
730
 * qui leurs sont relatifs
731
 *
732
 * @uses ecrire_plugin_actifs()
733
 *
734
 * @param bool $pipe_recherche ?
735
 * @return bool
736
 *     true si il y a eu des modifications sur la liste des plugins actifs, false sinon
737
 **/
738
function actualise_plugins_actifs($pipe_recherche = false) {
739
	return ecrire_plugin_actifs('', $pipe_recherche, 'force');
740
}
741
742
743
/**
744
 * Calcule ou modifie la liste des plugins actifs et recompile les fichiers caches
745
 * qui leurs sont relatifs
746
 *
747
 * @note
748
 *   Les  ecrire_meta() doivent en principe aussi initialiser la valeur a vide
749
 *   si elle n'existe pas risque de pb en php5 a cause du typage ou de null
750
 *   (verifier dans la doc php)
751
 *
752
 * @param string|string[] $plugin
753
 *     Plugin ou plugins concernés (leur chemin depuis le répertoire plugins)
754
 * @param bool $pipe_recherche
755
 *     ?
756
 * @param string $operation
757
 *     - raz : recalcule tout
758
 *     - ajoute : ajoute le plugin indiqué à la liste des plugins actifs
759
 *     - enleve : enleve le plugin indiqué de la liste des plugins actifs
760
 *     - force  : ?
761
 * @return bool
762
 *     true si il y a eu des modifications sur la liste des plugins actifs, false sinon
763
 **/
764
function ecrire_plugin_actifs($plugin, $pipe_recherche = false, $operation = 'raz') {
765
766
	// creer le repertoire cache/ si necessaire ! (installation notamment)
767
	$cache = sous_repertoire(_DIR_CACHE, '', false, true);
768
769
	// Si on n'a ni cache accessible, ni connexion SQL, on ne peut pas faire grand chose encore.
770
	if (!$cache and !spip_connect()) {
771
		return false;
772
	}
773
774
	if ($operation != 'raz') {
775
		$plugin_valides = liste_chemin_plugin_actifs();
776
		$plugin_valides = is_plugin_dir($plugin_valides);
777
		if (defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) {
778
			$plugin_valides_supp = liste_chemin_plugin_actifs(_DIR_PLUGINS_SUPPL);
779
			$plugin_valides_supp = is_plugin_dir($plugin_valides_supp, _DIR_PLUGINS_SUPPL);
780
			$plugin_valides = array_merge($plugin_valides, $plugin_valides_supp);
781
		}
782
		// si des plugins sont en attentes (coches mais impossible a activer)
783
		// on les reinjecte ici
784
		if (isset($GLOBALS['meta']['plugin_attente'])
785
			and $a = unserialize($GLOBALS['meta']['plugin_attente'])
786
		) {
787
			$plugin_valides = $plugin_valides + liste_chemin_plugin($a);
788
		}
789
790
		if ($operation == 'ajoute') {
791
			$plugin = array_merge($plugin_valides, $plugin);
792
		} elseif ($operation == 'enleve') {
793
			$plugin = array_diff($plugin_valides, $plugin);
794
		} else {
795
			$plugin = $plugin_valides;
796
		}
797
	}
798
	$actifs_avant = isset($GLOBALS['meta']['plugin']) ? $GLOBALS['meta']['plugin'] : '';
799
800
	// si une fonction de gestion de dependances existe, l'appeler ici
801
	if ($ajouter_dependances = charger_fonction("ajouter_dependances", "plugins", true)) {
802
		$plugin = $ajouter_dependances($plugin);
803
	}
804
805
	// recharger le xml des plugins a activer
806
	// on force le reload ici, meme si le fichier xml n'a pas change
807
	// pour ne pas rater l'ajout ou la suppression d'un fichier fonctions/options/administrations
808
	// pourra etre evite quand on ne supportera plus les plugin.xml
809
	// en deplacant la detection de ces fichiers dans la compilation ci dessous
810
	list($infos, $liste) = liste_plugin_valides($plugin, true);
811
	// trouver l'ordre d'activation
812
	list($plugin_valides, $ordre, $reste) = plugin_trier($infos, $liste);
813
	if ($reste) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $reste of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
814
		plugins_erreurs($reste, $liste, $infos);
815
	}
816
817
	// Ignorer les plugins necessitant une lib absente
818
	// et preparer la meta d'entete Http
819
	$err = $msg = $header = array();
820
	foreach ($plugin_valides as $p => $resume) {
821
		// Les headers ne doivent pas indiquer les versions des extensions PHP, ni la version PHP
822
		if (0 !== strpos($p, 'PHP:') and $p !== 'PHP') {
823
			$header[] = $p . ($resume['version'] ? "(" . $resume['version'] . ")" : "");
824
		}
825
		if ($resume['dir']) {
826
			foreach ($infos[$resume['dir_type']][$resume['dir']]['lib'] as $l) {
827
				if (!find_in_path($l['nom'], 'lib/')) {
828
					$err[$p] = $resume;
829
					$msg[$p][] = $l;
830
					unset($plugin_valides[$p]);
831
				}
832
			}
833
		}
834
	}
835
	if ($err) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $err of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
836
		plugins_erreurs($err, '', $infos, $msg);
837
	}
838
839
	if (isset($GLOBALS['meta']['message_crash_plugins'])) {
840
		effacer_meta('message_crash_plugins');
841
	}
842
	ecrire_meta('plugin', serialize($plugin_valides));
843
	$liste = array_diff_key($liste, $plugin_valides);
844
	ecrire_meta('plugin_attente', serialize($liste));
845
	$header = strtolower(implode(",", $header));
846
	if (!isset($GLOBALS['spip_header_silencieux']) or !$GLOBALS['spip_header_silencieux']) {
847
		ecrire_fichier(_DIR_VAR . "config.txt",
848
			(defined('_HEADER_COMPOSED_BY') ? _HEADER_COMPOSED_BY : "Composed-By: SPIP") . ' ' . $GLOBALS['spip_version_affichee'] . " @ www.spip.net + " . $header);
849
	} else {
850
		@unlink(_DIR_VAR . "config.txt");
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
851
	}
852
	// generer charger_plugins_chemin.php
853
	plugins_precompile_chemin($plugin_valides, $ordre);
854
	// generer les fichiers
855
	// - charger_plugins_options.php
856
	// - charger_plugins_fonctions.php
857
	plugins_precompile_xxxtions($plugin_valides, $ordre);
858
	// charger les chemins des plugins et les fichiers d'options 
859
	// (qui peuvent déclarer / utiliser des pipelines, ajouter d'autres chemins)
860
	plugins_amorcer_plugins_actifs();
861
	// mise a jour de la matrice des pipelines
862
	$prepend_code = pipeline_matrice_precompile($plugin_valides, $ordre, $pipe_recherche);
863
	// generer le fichier _CACHE_PIPELINE
864
	pipeline_precompile($prepend_code);
865
866
	// attendre eventuellement l'invalidation du cache opcode
867
	#spip_attend_invalidation_opcode_cache();
868
869
	if (spip_connect()) {
870
		// lancer et initialiser les nouveaux crons !
871
		include_spip('inc/genie');
872
		genie_queue_watch_dist();
873
	}
874
875
	return ($GLOBALS['meta']['plugin'] != $actifs_avant);
876
}
877
878
/**
879
 * Écrit le fichier de déclaration des chemins (path) des plugins actifs
880
 * 
881
 * Le fichier créé, une fois exécuté permet à SPIP de rechercher
882
 * des fichiers dans les répertoires des plugins concernés.
883
 * 
884
 * @see _chemin() Utilisé pour déclarer les chemins.
885
 * @uses plugin_version_compatible()
886
 * @uses ecrire_fichier_php()
887
 * 
888
 * @param array $plugin_valides
889
 *     Couples (prefixe => description) des plugins qui seront actifs
890
 * @param array $ordre
891
 *     Couples (prefixe => infos complètes) des plugins qui seront actifs, dans l'ordre de leurs dépendances
892
**/
893
function plugins_precompile_chemin($plugin_valides, $ordre) {
894
	$chemins = array();
895
	$contenu = "";
896
	foreach ($ordre as $p => $info) {
897
		// $ordre peur contenir des plugins en attente et non valides pour ce hit
898
		if (isset($plugin_valides[$p])) {
899
			$dir_type = $plugin_valides[$p]['dir_type'];
900
			$plug = $plugin_valides[$p]['dir'];
901
			// definir le plugin, donc le path avant l'include du fichier options
902
			// permet de faire des include_spip pour attraper un inc_ du plugin
903
904
			$dir = $dir_type . ".'" . $plug . "/'";
905
906
			$prefix = strtoupper(preg_replace(',\W,', '_', $info['prefix']));
907
			if (
908
				$prefix !== "SPIP"
909
				and strpos($dir, ":") === false // exclure le cas des procure:
910
			) {
911
				$contenu .= "define('_DIR_PLUGIN_$prefix',$dir);\n";
912
				foreach ($info['chemin'] as $chemin) {
913
					if (!isset($chemin['version']) or plugin_version_compatible($chemin['version'],
914
							$GLOBALS['spip_version_branche'], 'spip')
915
					) {
916
						$dir = $chemin['path'];
917 View Code Duplication
						if (strlen($dir) and $dir{0} == "/") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
918
							$dir = substr($dir, 1);
919
						}
920
						if (strlen($dir) and $dir == "./") {
921
							$dir = '';
922
						}
923
						if (strlen($dir)) {
924
							$dir = rtrim($dir, '/') . '/';
925
						}
926 View Code Duplication
						if (!isset($chemin['type']) or $chemin['type'] == 'public') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
927
							$chemins['public'][] = "_DIR_PLUGIN_$prefix" . (strlen($dir) ? ".'$dir'" : "");
928
						}
929 View Code Duplication
						if (!isset($chemin['type']) or $chemin['type'] == 'prive') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
930
							$chemins['prive'][] = "_DIR_PLUGIN_$prefix" . (strlen($dir) ? ".'$dir'" : "");
931
						}
932
					}
933
				}
934
			}
935
		}
936
	}
937
	if (count($chemins)) {
938
		$contenu .= "if (_DIR_RESTREINT) _chemin(implode(':',array(" . implode(',',
939
				array_reverse($chemins['public'])) . ")));\n"
940
			. "else _chemin(implode(':',array(" . implode(',', array_reverse($chemins['prive'])) . ")));\n";
941
	}
942
943
	ecrire_fichier_php(_CACHE_PLUGINS_PATH, $contenu);
944
}
945
946
/**
947
 * Écrit les fichiers de chargement des fichiers d'options et de fonctions des plugins
948
 * 
949
 * Les onglets et menus déclarés dans le fichier paquet.xml des plugins sont également 
950
 * ajoutés au fichier de fonctions créé.
951
 * 
952
 * @uses plugin_ongletbouton()
953
 * @uses ecrire_fichier_php()
954
 * 
955
 * @param array $plugin_valides
956
 *     Couples (prefixe => description) des plugins qui seront actifs
957
 * @param array $ordre
958
 *     Couples (prefixe => infos complètes) des plugins qui seront actifs, dans l'ordre de leurs dépendances
959
**/
960
function plugins_precompile_xxxtions($plugin_valides, $ordre) {
961
	$contenu = array('options' => '', 'fonctions' => '');
962
	$boutons = array();
963
	$onglets = array();
964
	$sign = "";
965
966
	foreach ($ordre as $p => $info) {
967
		// $ordre peur contenir des plugins en attente et non valides pour ce hit
968
		if (isset($plugin_valides[$p])) {
969
			$dir_type = $plugin_valides[$p]['dir_type'];
970
			$plug = $plugin_valides[$p]['dir'];
971
			$dir = constant($dir_type);
972
			$root_dir_type = str_replace('_DIR_', '_ROOT_', $dir_type);
973
			if ($info['menu']) {
974
				$boutons = array_merge($boutons, $info['menu']);
975
			}
976
			if ($info['onglet']) {
977
				$onglets = array_merge($onglets, $info['onglet']);
978
			}
979
			foreach ($contenu as $charge => $v) {
980
				// si pas declare/detecte a la lecture du paquet.xml,
981
				// detecer a nouveau ici puisque son ajout ne provoque pas une modif du paquet.xml
982
				// donc ni sa relecture, ni sa detection
983
				if (!isset($info[$charge])
984
					and $dir // exclure le cas du plugin "SPIP"
985
					and strpos($dir, ":") === false // exclure le cas des procure:
986
					and file_exists("$dir$plug/paquet.xml") // uniquement pour les paquet.xml
987
				) {
988
					if (is_readable("$dir$plug/" . ($file = $info['prefix'] . "_" . $charge . ".php"))) {
989
						$info[$charge] = array($file);
990
					}
991
				}
992
				if (isset($info[$charge])) {
993
					$files = $info[$charge];
994
					foreach ($files as $k => $file) {
995
						// on genere un if file_exists devant chaque include
996
						// pour pouvoir garder le meme niveau d'erreur general
997
						$file = trim($file);
998
						if (!is_readable("$dir$plug/$file")
999
							// uniquement pour les paquet.xml
1000
							and file_exists("$dir$plug/paquet.xml")
1001
						) {
1002
							unset($info[$charge][$k]);
1003
						} else {
1004
							$_file = $root_dir_type . ".'$plug/$file'";
1005
							$contenu[$charge] .= "include_once_check($_file);\n";
1006
						}
1007
					}
1008
				}
1009
			}
1010
			$sign .= md5(serialize($info));
1011
		}
1012
	}
1013
1014
	$contenu['options'] = "define('_PLUGINS_HASH','" . md5($sign) . "');\n" . $contenu['options'];
1015
	$contenu['fonctions'] .= plugin_ongletbouton("boutons_plugins", $boutons)
1016
		. plugin_ongletbouton("onglets_plugins", $onglets);
1017
1018
	ecrire_fichier_php(_CACHE_PLUGINS_OPT, $contenu['options']);
1019
	ecrire_fichier_php(_CACHE_PLUGINS_FCT, $contenu['fonctions']);
1020
}
1021
1022
/**
1023
 * Compile les entrées d'un menu et retourne le code php d'exécution
1024
 *
1025
 * Génère et retourne un code php (pour enregistrement dans un fichier de cache)
1026
 * permettant d'obtenir la liste des entrées de menus, ou des onglets
1027
 * de l'espace privé.
1028
 *
1029
 * Définit également une constante (_UPDATED_$nom et _UPDATED_md5_$nom),
1030
 * signalant une modification de ces menus
1031
 *
1032
 * @param string $nom Nom du type de menu
1033
 *     Exemple: boutons_plugins, onglets_plugins
1034
 * @param array $val Liste des entrées de ce menu
1035
 * @return string Code php
1036
 */
1037
function plugin_ongletbouton($nom, $val) {
1038
	if (!$val) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $val of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1039
		$val = array();
1040
	}
1041
1042
	$val = serialize($val);
1043
	$md5 = md5($val);
1044
1045
	if (!defined("_UPDATED_$nom")) {
1046
		define("_UPDATED_$nom", $val);
1047
		define("_UPDATED_md5_$nom", $md5);
1048
	}
1049
	$val = "unserialize('" . str_replace("'", "\'", $val) . "')";
1050
1051
	return
1052
		"if (!function_exists('$nom')) {\n"
1053
		. "function $nom(){return defined('_UPDATED_$nom')?unserialize(_UPDATED_$nom):$val;}\n"
1054
		. "function md5_$nom(){return defined('_UPDATED_md5_$nom')?_UPDATED_md5_$nom:'" . $md5 . "';}\n"
1055
		. "}\n";
1056
}
1057
1058
/**
1059
 * Chargement des plugins actifs dans le path de SPIP
1060
 * et exécution de fichiers d'options des plugins 
1061
 *
1062
 * Les fichiers d'options peuvent déclarer des pipelines ou de
1063
 * nouveaux chemins.
1064
 * 
1065
 * La connaissance chemins peut être nécessaire pour la construction
1066
 * du fichier d'exécution des pipelines. 
1067
**/
1068
function plugins_amorcer_plugins_actifs() {
1069
1070
	if (@is_readable(_CACHE_PLUGINS_PATH)) {
1071
		include_once(_CACHE_PLUGINS_PATH);
1072
	} 
1073
1074
	if (@is_readable(_CACHE_PLUGINS_OPT)) {
1075
		include_once(_CACHE_PLUGINS_OPT);
1076
	} else {
1077
		spip_log("pipelines desactives: impossible de produire " . _CACHE_PLUGINS_OPT);
1078
	}
1079
}
1080
1081
/**
1082
 * Crée la liste des filtres à traverser pour chaque pipeline
1083
 *
1084
 * Complète la globale `spip_pipeline` des fonctions que doit traverser un pipeline,
1085
 * et la globale `spip_matrice` des fichiers à charger qui contiennent ces fonctions.
1086
 * 
1087
 * Retourne aussi pour certaines balises présentes dans les paquet.xml (script, style, genie),
1088
 * un code PHP à insérer au début de la chaîne du ou des pipelines associés à cette balise
1089
 * (insert_head, insert_head_css, taches_generales_cron, ...). Ce sont des écritures 
1090
 * raccourcies pour des usages fréquents de ces pipelines.
1091
 * 
1092
 * @param array $plugin_valides
1093
 *     Couples (prefixe => description) des plugins qui seront actifs
1094
 * @param array $ordre
1095
 *     Couples (prefixe => infos complètes) des plugins qui seront actifs, dans l'ordre de leurs dépendances
1096
 * @param string $pipe_recherche
1097
 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
1098
 *     Couples (nom du pipeline => Code PHP à insérer au début du pipeline)
1099
**/
1100
function pipeline_matrice_precompile($plugin_valides, $ordre, $pipe_recherche) {
1101
	static $liste_pipe_manquants = array();
1102
	if (($pipe_recherche) && (!in_array($pipe_recherche, $liste_pipe_manquants))) {
1103
		$liste_pipe_manquants[] = $pipe_recherche;
1104
	}
1105
1106
	$prepend_code = array();
1107
1108
	foreach ($ordre as $p => $info) {
1109
		// $ordre peur contenir des plugins en attente et non valides pour ce hit
1110
		if (isset($plugin_valides[$p])) {
1111
			$dir_type = $plugin_valides[$p]['dir_type'];
1112
			$root_dir_type = str_replace('_DIR_', '_ROOT_', $dir_type);
1113
			$plug = $plugin_valides[$p]['dir'];
1114
			$prefix = (($info['prefix'] == "spip") ? "" : $info['prefix'] . "_");
1115
			if (isset($info['pipeline']) and is_array($info['pipeline'])) {
1116
				foreach ($info['pipeline'] as $pipe) {
1117
					$nom = $pipe['nom'];
1118
					if (isset($pipe['action'])) {
1119
						$action = $pipe['action'];
1120
					} else {
1121
						$action = $nom;
1122
					}
1123
					$nomlower = strtolower($nom);
1124
					if ($nomlower != $nom
1125
						and isset($GLOBALS['spip_pipeline'][$nom])
1126
						and !isset($GLOBALS['spip_pipeline'][$nomlower])
1127
					) {
1128
						$GLOBALS['spip_pipeline'][$nomlower] = $GLOBALS['spip_pipeline'][$nom];
1129
						unset($GLOBALS['spip_pipeline'][$nom]);
1130
					}
1131
					$nom = $nomlower;
1132
					// une action vide est une declaration qui ne doit pas etre compilee !
1133
					if (!isset($GLOBALS['spip_pipeline'][$nom])) // creer le pipeline eventuel
1134
					{
1135
						$GLOBALS['spip_pipeline'][$nom] = "";
1136
					}
1137
					if ($action) {
1138
						if (strpos($GLOBALS['spip_pipeline'][$nom], "|$prefix$action") === false) {
1139
							$GLOBALS['spip_pipeline'][$nom] = preg_replace(",(\|\||$),", "|$prefix$action\\1",
1140
								$GLOBALS['spip_pipeline'][$nom], 1);
1141
						}
1142
						if (isset($pipe['inclure'])) {
1143
							$GLOBALS['spip_matrice']["$prefix$action"] =
1144
								"$root_dir_type:$plug/" . $pipe['inclure'];
1145
						}
1146
					}
1147
				}
1148
			}
1149
			if (isset($info['genie']) and count($info['genie'])) {
1150
				if (!isset($prepend_code['taches_generales_cron'])) {
1151
					$prepend_code['taches_generales_cron'] = "";
1152
				}
1153
				foreach ($info['genie'] as $genie) {
1154
					$nom = $prefix . $genie['nom'];
1155
					$periode = max(60, intval($genie['periode']));
1156
					if (charger_fonction($nom, "genie", true)) {
1157
						$prepend_code['taches_generales_cron'] .= "\$val['$nom'] = $periode;\n";
1158
					} else {
1159
						spip_log("Fonction genie_$nom introuvable", _LOG_ERREUR);
1160
					}
1161
				}
1162
			}
1163
			if (isset($info['style']) and count($info['style'])) {
1164
				if (!isset($prepend_code['insert_head_css'])) {
1165
					$prepend_code['insert_head_css'] = "";
1166
				}
1167
				if (!isset($prepend_code['header_prive_css'])) {
1168
					$prepend_code['header_prive_css'] = "";
1169
				}
1170
				foreach ($info['style'] as $style) {
1171 View Code Duplication
					if (isset($style['path']) and $style['path']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1172
						$code = "if (\$f=timestamp(direction_css(find_in_path('" . addslashes($style['path']) . "')))) ";
1173
					} else {
1174
						$code = "if (\$f='" . addslashes($style['url']) . "') ";
1175
					}
1176
					$code .= "\$val .= '<link rel=\"stylesheet\" href=\"'.\$f.'\" type=\"text/css\"";
1177
					if (isset($style['media']) and strlen($style['media'])) {
1178
						$code .= " media=\"" . addslashes($style['media']) . "\"";
1179
					}
1180
					$code .= "/>';\n";
1181
					if ($style['type'] != 'prive') {
1182
						$prepend_code['insert_head_css'] .= $code;
1183
					}
1184
					if ($style['type'] != 'public') {
1185
						$prepend_code['header_prive_css'] .= $code;
1186
					}
1187
				}
1188
			}
1189
			if (!isset($prepend_code['insert_head'])) {
1190
				$prepend_code['insert_head'] = "";
1191
			}
1192
			if (!isset($prepend_code['header_prive'])) {
1193
				$prepend_code['header_prive'] = "";
1194
			}
1195
			if (isset($info['script']) and count($info['script'])) {
1196
				foreach ($info['script'] as $script) {
1197 View Code Duplication
					if (isset($script['path']) and $script['path']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1198
						$code = "if (\$f=timestamp(find_in_path('" . addslashes($script['path']) . "'))) ";
1199
					} else {
1200
						$code = "if (\$f='" . addslashes($script['url']) . "') ";
1201
					}
1202
					$code .= "\$val .= '<script src=\"'.\$f.'\" type=\"text/javascript\"></script>';\n";
1203
					if ($script['type'] != 'prive') {
1204
						$prepend_code['insert_head'] .= $code;
1205
					}
1206
					if ($script['type'] != 'public') {
1207
						$prepend_code['header_prive'] .= $code;
1208
					}
1209
				}
1210
			}
1211
		}
1212
	}
1213
1214
	$prepend_code['insert_head'] =
1215
		"include_once_check(_DIR_RESTREINT . 'inc/pipelines.php');\n"
1216
		. "\$val = minipipe('f_jQuery', \$val);\n"
1217
		. $prepend_code['insert_head'];
1218
	$prepend_code['header_prive'] =
1219
		"include_once_check(_DIR_RESTREINT . 'inc/pipelines_ecrire.php');\n"
1220
		. "\$val = minipipe('f_jQuery_prive', \$val);\n"
1221
		. $prepend_code['header_prive'];
1222
1223
	// on ajoute les pipe qui ont ete recenses manquants
1224
	foreach ($liste_pipe_manquants as $add_pipe) {
1225
		if (!isset($GLOBALS['spip_pipeline'][$add_pipe])) {
1226
			$GLOBALS['spip_pipeline'][$add_pipe] = '';
1227
		}
1228
	}
1229
1230
	return $prepend_code;
1231
}
1232
1233
/**
1234
 * Précompilation des pipelines
1235
 *
1236
 * Crée le fichier d'exécution des pipelines 
1237
 * dont le chemin est défini par `_CACHE_PIPELINES`
1238
 * 
1239
 * La liste des pipelines est définie par la globale `spip_pipeline`
1240
 * qui a été remplie soit avec les fichiers d'options, soit avec 
1241
 * des descriptions de plugins (plugin.xml ou paquet.xml) dont celui de SPIP lui-même.
1242
 * 
1243
 * Les fichiers à charger pour accéder aux fonctions qui doivent traverser
1244
 * un pipeline se trouve dans la globale `spip_matrice`.
1245
 * 
1246
 * @see pipeline_matrice_precompile()
1247
 * 
1248
 * @uses ecrire_fichier_php()
1249
 * @uses clear_path_cache()
1250
 * 
1251
 * @param array $prepend_code
1252
 *     Code PHP à insérer avant le passage dans la chaîne des fonctions d'un pipeline
1253
 *     Couples 'Nom du pipeline' => Code PHP à insérer
1254
**/
1255
function pipeline_precompile($prepend_code = array()) {
1256
1257
	$content = "";
1258
	foreach ($GLOBALS['spip_pipeline'] as $action => $pipeline) {
1259
		$s_inc = "";
1260
		$s_call = "";
1261
		$pipe = array_filter(explode('|', $pipeline));
1262
		// Eclater le pipeline en filtres et appliquer chaque filtre
1263
		foreach ($pipe as $fonc) {
1264
			$fonc = trim($fonc);
1265
			$s_call .= '$val = minipipe(\'' . $fonc . '\', $val);' . "\n";
1266
			if (isset($GLOBALS['spip_matrice'][$fonc])) {
1267
				$file = $GLOBALS['spip_matrice'][$fonc];
1268
				$file = "'$file'";
1269
				// si un _DIR_XXX: est dans la chaine, on extrait la constante
1270
				if (preg_match(",(_(DIR|ROOT)_[A-Z_]+):,Ums", $file, $regs)) {
1271
					$dir = $regs[1];
1272
					$root_dir = str_replace('_DIR_', '_ROOT_', $dir);
1273
					if (defined($root_dir)) {
1274
						$dir = $root_dir;
1275
					}
1276
					$file = str_replace($regs[0], "'." . $dir . ".'", $file);
1277
					$file = str_replace("''.", "", $file);
1278
					$file = str_replace(constant($dir), '', $file);
1279
				}
1280
				$s_inc .= "include_once_check($file);\n";
1281
			}
1282
		}
1283
		if (strlen($s_inc)) {
1284
			$s_inc = "static \$inc=null;\nif (!\$inc){\n$s_inc\$inc=true;\n}\n";
1285
		}
1286
		$content .= "// Pipeline $action \n"
1287
			. "function execute_pipeline_$action(&\$val){\n"
1288
			. $s_inc
1289
			. ((isset($prepend_code[$action]) and strlen($prepend_code[$action])) ? trim($prepend_code[$action]) . "\n" : '')
1290
			. $s_call
1291
			. "return \$val;\n}\n";
1292
	}
1293
	ecrire_fichier_php(_CACHE_PIPELINES, $content);
1294
	clear_path_cache();
1295
}
1296
1297
1298
/**
1299
 * Indique si un chemin de plugin fait parti des plugins activés sur le site
1300
 *
1301
 * @param string $plug_path
1302
 *     Chemin du plugin
1303
 * @return bool
1304
 *     true si le plugin est actif, false sinon
1305
**/
1306
function plugin_est_installe($plug_path) {
1307
	$plugin_installes = isset($GLOBALS['meta']['plugin_installes']) ? unserialize($GLOBALS['meta']['plugin_installes']) : array();
1308
	if (!$plugin_installes) {
1309
		return false;
1310
	}
1311
1312
	return in_array($plug_path, $plugin_installes);
1313
}
1314
1315
1316
/**
1317
 * Parcours les plugins activés et appelle leurs fonctions d'installation si elles existent.
1318
 *
1319
 * Elle ajoute ensuite les plugins qui ont été installés dans la valeur "plugin_installes"
1320
 * de la table meta. Cette meta ne contient que les noms des plugins qui ont une version_base.
1321
 *
1322
 * @uses plugins_installer_dist()
1323
 **/
1324
function plugin_installes_meta() {
1325
	$installer_plugins = charger_fonction('installer', 'plugins');
1326
	$meta_plug_installes = array();
1327
	foreach (unserialize($GLOBALS['meta']['plugin']) as $prefix => $resume) {
1328
		if ($plug = $resume['dir']) {
1329
			$infos = $installer_plugins($plug, 'install', $resume['dir_type']);
1330
			if ($infos) {
1331
				if (!is_array($infos) or $infos['install_test'][0]) {
1332
					$meta_plug_installes[] = $plug;
1333
				}
1334
				if (is_array($infos)) {
1335
					list($ok, $trace) = $infos['install_test'];
1336
					include_spip('inc/filtres_boites');
1337
					echo "<div class='install-plugins svp_retour'>"
1338
						. boite_ouvrir(_T('plugin_titre_installation', array('plugin' => typo($infos['nom']))),
1339
							($ok ? 'success' : 'error'))
1340
						. $trace
1341
						. "<div class='result'>"
1342
						. ($ok ? ((isset($infos['upgrade']) && $infos['upgrade']) ? _T("plugin_info_upgrade_ok") : _T("plugin_info_install_ok")) : _T("avis_operation_echec"))
1343
						. "</div>"
1344
						. boite_fermer()
1345
						. "</div>";
1346
				}
1347
			}
1348
		}
1349
	}
1350
	ecrire_meta('plugin_installes', serialize($meta_plug_installes), 'non');
1351
}
1352
1353
/**
1354
 * Écrit un fichier PHP
1355
 *
1356
 * @param string $nom
1357
 *     Chemin du fichier
1358
 * @param string $contenu
1359
 *     Contenu du fichier (sans les balises ouvrantes et fermantes de PHP)
1360
 * @param string $comment
1361
 *     Commentaire : code écrit en tout début de fichier, après la balise PHP ouvrante
1362
**/
1363
function ecrire_fichier_php($nom, $contenu, $comment = '') {
1364
	ecrire_fichier($nom,
1365
		'<' . '?php' . "\n" . $comment . "\nif (defined('_ECRIRE_INC_VERSION')) {\n" . $contenu . "}\n?" . '>');
1366
}
1367