[SPIP] ~v3.2.3-->v3.2.4
[lhc/web/www.git] / www / plugins-dist / svp / inc / svp_outiller.php
1 <?php
2
3 /**
4 * Fichier de fonctions
5 *
6 * @plugin SVP pour SPIP
7 * @license GPL
8 * @package SPIP\SVP\Fonctions
9 **/
10
11 if (!defined('_ECRIRE_INC_VERSION')) {
12 return;
13 }
14
15
16 if (!defined('_SVP_VERSION_SPIP_MIN')) {
17 /**
18 * Version SPIP minimale quand un plugin ne le precise pas
19 *
20 * Version SPIP correspondant à l'apparition des plugins */
21 define('_SVP_VERSION_SPIP_MIN', '1.9.0');
22 }
23
24 if (!defined('_SVP_VERSION_SPIP_MAX')) {
25 /**
26 * Version SPIP maximale
27 *
28 * Pour l'instant on ne connait pas la borne sup exacte */
29 define('_SVP_VERSION_SPIP_MAX', '3.2.99');
30 }
31
32 /**
33 * Liste des branches significatives de SPIP et de leurs bornes (versions min et max)
34 *
35 * À mettre a jour en fonction des sorties
36 *
37 * @global array $GLOBALS ['infos_branches_spip']
38 */
39 $GLOBALS['infos_branches_spip'] = array(
40 '1.9' => array(_SVP_VERSION_SPIP_MIN, '1.9.2'),
41 '2.0' => array('2.0.0', '2.0.99'),
42 '2.1' => array('2.1.0', '2.1.99'),
43 '3.0' => array('3.0.0', '3.0.99'),
44 '3.1' => array('3.1.0', '3.1.99'),
45 '3.2' => array('3.2.0', _SVP_VERSION_SPIP_MAX)
46 );
47 # define('_INFOS_BRANCHES_SPIP', serialize($infos_branches_spip));
48
49 /**
50 * Liste des licences de plugin
51 *
52 * @global array $GLOBALS ['licences_plugin']
53 */
54 $GLOBALS['licences_plugin'] = array(
55 'apache' => array(
56 'versions' => array('2.0', '1.1', '1.0'),
57 'nom' => 'Apache licence, version @version@',
58 'url' => 'http://www.apache.org/licenses/LICENSE-@version@'
59 ),
60 'art' => array(
61 'versions' => array('1.3'),
62 'nom' => 'Art libre @version@',
63 'url' => 'http://artlibre.org/licence/lal'
64 ),
65 'mit' => array(
66 'versions' => array(),
67 'nom' => 'MIT',
68 'url' => 'http://opensource.org/licenses/mit-license.php'
69 ),
70 'bsd' => array(
71 'versions' => array(),
72 'nom' => 'BSD',
73 'url' => 'http://www.freebsd.org/copyright/license.html'
74 ),
75 'agpl' => array(
76 'versions' => array('3'),
77 'nom' => 'AGPL @version@',
78 'url' => 'http://www.gnu.org/licenses/agpl.html'
79 ),
80 'fdl' => array(
81 'versions' => array('1.3', '1.2', '1.1'),
82 'nom' => 'FDL @version@',
83 'url' => 'http://www.gnu.org/licenses/fdl-@version@.html'
84 ),
85 'lgpl' => array(
86 'versions' => array('3.0', '2.1'),
87 'nom' => array('3.0' => 'LGPL 3', '2.1' => 'LGPL 2.1'),
88 'url' => 'http://www.gnu.org/licenses/lgpl-@version@.html'
89 ),
90 'gpl' => array(
91 'versions' => array('3', '2', '1'),
92 'nom' => 'GPL @version@',
93 'url' => 'http://www.gnu.org/licenses/gpl-@version@.0.html'
94 ),
95 'ccby' => array(
96 'versions' => array('2.0', '2.5', '3.0'),
97 'suffixes' => array('-sa', '-nc', '-nd', '-nc-nd', '-nc-sa'),
98 'nom' => 'CC BY@suffixe@ @version@',
99 'url' => 'http://creativecommons.org/licenses/by@suffixe@/@version@/'
100 )
101 );
102 # define('_LICENCES_PLUGIN', serialize($licences_plugin));
103
104 /**
105 * Fusionne 2 intervalles de compatibilité
106 *
107 * Soit '[1.9;2.1]' et '[2.1;3.0.*]', la fonction retourne '[1.9;3.0.*]'
108 *
109 * En gros la fonction est utilisé pour calculer l'intervalle de validité
110 * d'un plugin ayant plusieurs paquets avec des compatibilités différentes.
111 * La compatibilité du plugin est le total de toutes les compatibilités.
112 *
113 * @uses extraire_bornes()
114 * @uses construire_intervalle()
115 * @param string $intervalle_a
116 * Intervalle de compatibilité
117 * @param string $intervalle_b
118 * Intervalle de compatibilité
119 * @return string
120 * Intervalle de compatibilité
121 **/
122 function fusionner_intervalles($intervalle_a, $intervalle_b) {
123
124 // On recupere les bornes de chaque intervalle
125 $borne_a = extraire_bornes($intervalle_a);
126 $borne_b = extraire_bornes($intervalle_b);
127
128 // On initialise la borne min de chaque intervalle a 1.9.0 inclus si vide
129 if (!$borne_a['min']['valeur']) {
130 $borne_a['min']['valeur'] = _SVP_VERSION_SPIP_MIN;
131 $borne_a['min']['incluse'] = true;
132 }
133 if (!$borne_b['min']['valeur']) {
134 $borne_b['min']['valeur'] = _SVP_VERSION_SPIP_MIN;
135 $borne_b['min']['incluse'] = true;
136 }
137
138 // On initialise la borne max de chaque intervalle a la version SPIP max incluse si vide
139 if (!$borne_a['max']['valeur']) {
140 $borne_a['max']['valeur'] = _SVP_VERSION_SPIP_MAX;
141 $borne_a['max']['incluse'] = true;
142 }
143 if (!$borne_b['max']['valeur']) {
144 $borne_b['max']['valeur'] = _SVP_VERSION_SPIP_MAX;
145 $borne_b['max']['incluse'] = true;
146 }
147
148 // On calcul maintenant :
149 // -- la borne min de l'intervalle fusionne = min(min_a, min_b)
150 if (spip_version_compare($borne_a['min']['valeur'], $borne_b['min']['valeur'], '<=')) {
151 $bornes_fusionnees['min'] = $borne_a['min'];
152 } else {
153 $bornes_fusionnees['min'] = $borne_b['min'];
154 }
155 // -- la borne max de l'intervalle fusionne = max(max_a, max_b)
156 if (spip_version_compare($borne_a['max']['valeur'], $borne_b['max']['valeur'], '<=')) {
157 $bornes_fusionnees['max'] = $borne_b['max'];
158 } else {
159 $bornes_fusionnees['max'] = $borne_a['max'];
160 }
161
162 return construire_intervalle($bornes_fusionnees);
163 }
164
165 /**
166 * Extrait les valeurs d'un intervalle de compatibilité.
167 *
168 * Calcule les valeurs min, max et si ces valeurs sont intégrées ou non
169 * à l'intervalle.
170 *
171 * @param string $intervalle
172 * Intervalle de compatibilité, tel que '[2.1;3.0]'
173 * @param bool $initialiser
174 * - True pour mettre les valeurs connues mini et maxi de SPIP lorsque
175 * les bornes ne sont pas renseignées dans l'intervalle.
176 * - False pour ne rien mettre sinon.
177 * @return array
178 * Tableau avec les index :
179 * - min : la borne inférieure, qui contient les index 'valeur' et 'incluse'
180 * - max : la borne supérieure, qui contient les index 'valeur' et 'incluse'
181 * Le sous index 'incluse' vaut true si cette borne est incluse dans l'intervalle.
182 **/
183 function extraire_bornes($intervalle, $initialiser = false) {
184 static $borne_vide = array('valeur' => '', 'incluse' => false);
185 static $borne_inf_init = array('valeur' => _SVP_VERSION_SPIP_MIN, 'incluse' => true);
186 static $borne_sup_init = array('valeur' => _SVP_VERSION_SPIP_MAX, 'incluse' => true);
187
188 if ($initialiser) {
189 $bornes = array('min' => $borne_inf_init, 'max' => $borne_sup_init);
190 } else {
191 $bornes = array('min' => $borne_vide, 'max' => $borne_vide);
192 }
193
194 if ($intervalle
195 and preg_match(',^[\[\(\]]([0-9.a-zRC\s\-]*)[;]([0-9.a-zRC\s\-\*]*)[\]\)\[]$,Uis', $intervalle, $matches)
196 ) {
197 if ($matches[1]) {
198 $bornes['min']['valeur'] = trim($matches[1]);
199 $bornes['min']['incluse'] = ($intervalle{0} == "[");
200 }
201 if ($matches[2]) {
202 $bornes['max']['valeur'] = trim($matches[2]);
203 $bornes['max']['incluse'] = (substr($intervalle, -1) == "]");
204 }
205 }
206
207 return $bornes;
208 }
209
210 /**
211 * Contruit un intervalle de compatibilité
212 *
213 * @param array $bornes
214 * L'intervalle décrit sous forme de tableau avec pour index :
215 * - min : la borne inférieure, qui contient les index 'valeur' et 'incluse'
216 * - max : la borne supérieure, qui contient les index 'valeur' et 'incluse'
217 * Le sous index 'incluse' vaut true si cette borne est incluse dans l'intervalle.
218 * @param string $dtd
219 * DTD de destination (paquet ou plugin) qui influera sur l'écriture à faire
220 * en utilisant des parenthèses ou des crochets pour définir l'exclusion d'une intervalle
221 * tel que ']2.1.2,3.0.1[' (paquet) ou '(2.1.2,3.0.1)' (plugin)
222 * @return string
223 * Intervalle de compatibilité tel que '[2.1;3.0]'
224 **/
225 function construire_intervalle($bornes, $dtd = 'paquet') {
226 return ($bornes['min']['incluse'] ? '[' : ($dtd == 'paquet' ? ']' : '('))
227 . $bornes['min']['valeur'] . ';' . $bornes['max']['valeur']
228 . ($bornes['max']['incluse'] ? ']' : ($dtd == 'paquet' ? '[' : ')'));
229 }
230
231
232 /**
233 * Retourne la liste des branches de SPIP comprises dans un intervalle
234 * de compatibilité donné.
235 *
236 * @uses extraire_bornes()
237 * @param string $intervalle
238 * Intervalle de compatibilité, tel que [2.0.0;3.0.0]
239 * @return string
240 * Branches de SPIP séparées par des virgules, tel que 2.0,2.1,3.0
241 **/
242 function compiler_branches_spip($intervalle) {
243 include_spip('plugins/installer');
244
245 global $infos_branches_spip;
246 $liste_branches_spip = array_keys($GLOBALS['infos_branches_spip']);
247
248 $bornes = extraire_bornes($intervalle, false);
249 // On traite d'abord les cas ou l'intervalle est :
250 // - vide
251 // - non vide mais avec les deux bornes vides
252 // Dans ces cas la compatibilite est totale, on renvoie toutes les branches
253 if (!$intervalle or (!$bornes['min']['valeur'] and !$bornes['max']['valeur'])) {
254 return implode(',', $liste_branches_spip);
255 }
256
257 // On force l'initialisation des bornes et on les nettoie des suffixes d'etat
258 $bornes = extraire_bornes($intervalle, true);
259 // Si les bornes sont en dehors de l'intervalle [_SVP_VERSION_SPIP_MIN;_SVP_VERSION_SPIP_MAX] on le reduit
260 if (spip_version_compare($bornes['min']['valeur'], _SVP_VERSION_SPIP_MIN, '<')) {
261 $bornes['min']['valeur'] = _SVP_VERSION_SPIP_MIN;
262 $bornes['min']['incluse'] = true;
263 }
264 if (spip_version_compare(_SVP_VERSION_SPIP_MAX, $bornes['max']['valeur'], '<=')) {
265 $bornes['max']['valeur'] = _SVP_VERSION_SPIP_MAX;
266 $bornes['max']['incluse'] = true;
267 }
268 // On les nettoie des suffixes d'etat
269 $borne_inf = strtolower(preg_replace(',([0-9])[\s-.]?(dev|alpha|a|beta|b|rc|pl|p),i', '\\1',
270 $bornes['min']['valeur']));
271 $borne_sup = strtolower(preg_replace(',([0-9])[\s-.]?(dev|alpha|a|beta|b|rc|pl|p),i', '\\1',
272 $bornes['max']['valeur']));
273
274 // On determine les branches inf et sup issues du phrasage de l'intervalle
275 // -- on initialise la branche inf de l'intervalle que l'on va preciser ensuite
276 $t = explode('.', $borne_inf);
277 $branche_inf = $t[0] . '.' . $t[1];
278 // -- pour eviter toutes erreur fatale on verifie que la branche est bien dans la liste des possibles
279 // -- -> si non, on renvoie vide
280 if (!in_array($branche_inf, $liste_branches_spip)) {
281 return '';
282 }
283 // -- on complete la borne inf de l'intervalle de x.y en x.y.z et on determine la vraie branche
284 if (!isset($t[2]) or !$t[2]) {
285 if ($bornes['min']['incluse']) {
286 $borne_inf = $infos_branches_spip[$branche_inf][0];
287 } else {
288 $branche_inf = $liste_branches_spip[array_search($branche_inf, $liste_branches_spip) + 1];
289 $borne_inf = $infos_branches_spip[$branche_inf][0];
290 }
291 }
292
293 // -- on initialise la branche sup de l'intervalle que l'on va preciser ensuite
294 $t = explode('.', $borne_sup);
295 // des gens mettent juste * (pas glop)
296 $branche_sup = $t[0] . (isset($t[1]) ? '.' . $t[1] : '');
297
298 // -- pour eviter toutes erreur fatale on verifie que la branche est bien dans la liste des possibles
299 // -- -> si non, on renvoie vide
300 if (!in_array($branche_sup, $liste_branches_spip)) {
301 return '';
302 }
303 // -- on complete la borne sup de l'intervalle de x.y en x.y.z et on determine la vraie branche
304 if (!isset($t[2]) or !$t[2]) {
305 if ($bornes['max']['incluse']) {
306 $borne_sup = $infos_branches_spip[$branche_sup][1];
307 } else {
308 $branche_sup = $liste_branches_spip[array_search($branche_sup, $liste_branches_spip) - 1];
309 $borne_sup = $infos_branches_spip[$branche_sup][1];
310 }
311 }
312
313 // -- on verifie que les bornes sont bien dans l'ordre :
314 // -> sinon on retourne la branche sup uniquement
315 if (spip_version_compare($borne_inf, $borne_sup, '>=')) {
316 return $branche_sup;
317 }
318
319 // A ce stade, on a un intervalle ferme en bornes ou en branches
320 // Il suffit de trouver les branches qui y sont incluses, sachant que les branches inf et sup
321 // le sont a coup sur maintenant
322 $index_inf = array_search($branche_inf, $liste_branches_spip);
323 $index_sup = array_search($branche_sup, $liste_branches_spip);
324 $liste = array();
325 for ($i = $index_inf; $i <= $index_sup; $i++) {
326 $liste[] = $liste_branches_spip[$i];
327 }
328
329 return implode(',', $liste);
330 }
331
332
333 /**
334 * Transforme un texte écrit en entités HTML, dans le charset du site
335 *
336 * @param string $texte
337 * Texte avec des entités HTML
338 * @param string $charset
339 * @return string $texte
340 * Texte dans le charset du site
341 **/
342 function entite2charset($texte, $charset = null) {
343 if (!strlen($texte)) {
344 return '';
345 }
346 if (!$charset) {
347 $charset = $GLOBALS['meta']['charset'];
348 }
349 include_spip('inc/charsets');
350
351 return unicode2charset(html_entity_decode(preg_replace('/&([lg]t;)/S', '&amp;\1', $texte), ENT_NOQUOTES, $charset));
352 }
353
354 /**
355 * Teste si 2 balises XML sont identiques
356 *
357 * @param array|string $balise1
358 * Balise à comparer
359 * @param array|string $balise2
360 * Balise à comparer
361 * @return bool
362 * True si elles sont identiques, false sinon.
363 **/
364 function balise_identique($balise1, $balise2) {
365 if (is_array($balise1)) {
366 foreach ($balise1 as $_attribut1 => $_valeur1) {
367 if (!array_key_exists($_attribut1, $balise2)) {
368 return false;
369 } else {
370 if ($_valeur1 != $balise2[$_attribut1]) {
371 return false;
372 }
373 }
374 }
375
376 return true;
377 } else {
378 return ($balise1 == $balise2);
379 }
380 }
381
382
383 /**
384 * Déterminer la licence exacte avec un nom et un lien de doc standardisé
385 *
386 * @param string $prefixe
387 * Préfixe de la licence tel que gnu, free, cc, creative common
388 * @param string $nom
389 * Nom de la licence tel que gpl, lgpl, agpl, fdl, mit, bsd...
390 * @param string $suffixe
391 * Suffixe de la licence tel que licence, -sharealike, -nc-nd ...
392 * @param string $version
393 * Version de la licence tel que 3.0
394 * @return array
395 * Si la licence est connu, retourne 2 index :
396 * - nom : le nom le la licence
397 * - url : lien vers la licence
398 */
399 function definir_licence($prefixe, $nom, $suffixe, $version) {
400 global $licences_plugin;
401 $licence = array();
402
403 $prefixe = strtolower($prefixe);
404 $nom = strtolower($nom);
405 $suffixe = strtolower($suffixe);
406
407 if (((trim($prefixe) == 'creative common') and ($nom == 'attribution'))
408 or (($prefixe == 'cc') and ($nom == 'by'))
409 ) {
410 $nom = 'ccby';
411 }
412
413 if (array_key_exists($nom, $licences_plugin)) {
414 if (!$licences_plugin[$nom]['versions']) {
415 // La licence n'est pas versionnee : on affecte donc directement le nom et l'url
416 $licence['nom'] = $licences_plugin[$nom]['nom'];
417 $licence['url'] = $licences_plugin[$nom]['url'];
418 } else {
419 // Si la version est pas bonne on prend la plus recente
420 if (!$version or !in_array($version, $licences_plugin[$nom]['versions'], true)) {
421 $version = $licences_plugin[$nom]['versions'][0];
422 }
423 if (is_array($licences_plugin[$nom]['nom'])) {
424 $licence['nom'] = $licences_plugin[$nom]['nom'][$version];
425 } else {
426 $licence['nom'] = str_replace('@version@', $version, $licences_plugin[$nom]['nom']);
427 }
428 $licence['url'] = str_replace('@version@', $version, $licences_plugin[$nom]['url']);
429
430 if ($nom == 'ccby') {
431 if ($suffixe == '-sharealike') {
432 $suffixe = '-sa';
433 }
434 if (!$suffixe or !in_array($suffixe, $licences_plugin[$nom]['suffixes'], true)) {
435 $suffixe = '';
436 }
437 $licence['nom'] = str_replace('@suffixe@', strtoupper($suffixe), $licence['nom']);
438 $licence['url'] = str_replace('@suffixe@', $suffixe, $licence['url']);
439 }
440 }
441 }
442
443 return $licence;
444 }
445
446 /**
447 * Liste les librairies présentes
448 *
449 * Cherche des librairie dans tous les dossiers 'lib' présents dans chaque
450 * chemin déclaré (plugins, squelettes, SPIP). Un répertoire dans un dossier
451 * 'lib' est considéré comme une librairie, et le nom de ce répertoire est
452 * utilisé comme nom de la librairie.
453 *
454 * @return array
455 * Tableau de couples (nom de la librairie => répertoire de la librairie)
456 **/
457 function svp_lister_librairies() {
458 $libs = array();
459 foreach (array_reverse(creer_chemin()) as $d) {
460 if (is_dir($dir = $d . 'lib/') and $t = opendir($dir)) {
461 while (($f = readdir($t)) !== false) {
462 if ($f[0] != '.' and is_dir("$dir/$f")) {
463 $libs[$f] = $dir;
464 }
465 }
466 }
467 }
468
469 return $libs;
470 }
471
472
473 /**
474 * Retourne '00x.00y.00z' à partir de 'x.y.z'
475 *
476 * Retourne la chaine de la version x.y.z sous une forme normalisée
477 * permettant le tri naturel. On complète à gauche d'un nombre de zéro
478 * manquant pour aller à 3 caractères entre chaque point.
479 *
480 * @see denormaliser_version()
481 * @param string $version
482 * Numéro de version dénormalisée
483 * @return string
484 * Numéro de version normalisée
485 **/
486 function normaliser_version($version = '') {
487
488 $version_normalisee = '';
489
490 if (preg_match(',([0-9.]+)[\s-.]?(dev|alpha|a|beta|b|rc|pl|p)?,i', $version, $matches)) {
491 if (isset($matches[1]) and $matches[1]) {
492 $v = explode('.', $matches[1]);
493 $vn = array();
494 foreach ($v as $_nombre) {
495 $vn[] = str_pad($_nombre, 3, '0', STR_PAD_LEFT);
496 }
497 $version_normalisee = implode('.', $vn);
498 if (isset($matches[2]) and $matches[2]) {
499 $version_normalisee = $version_normalisee . '-' . $matches[2];
500 }
501 }
502 }
503
504 return $version_normalisee;
505 }