[ Index ] |
|
Code source de SPIP 1.9.2c |
1 <?php 2 3 /***************************************************************************\ 4 * SPIP, Systeme de publication pour l'internet * 5 * * 6 * Copyright (c) 2001-2007 * 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 // librairie pour parametrage plugin 16 // 17 define('_FILE_PLUGIN_CONFIG', "plugin.xml"); 18 19 // besoin de inc_meta 20 include_spip('inc/meta'); 21 22 // lecture des sous repertoire plugin existants 23 // http://doc.spip.org/@liste_plugin_files 24 function liste_plugin_files(){ 25 static $plugin_files=array(); 26 if (!count($plugin_files)){ 27 foreach (preg_files(_DIR_PLUGINS, '/plugin[.]xml$') as $plugin) { 28 $plugin_files[]=substr(dirname($plugin), strlen(_DIR_PLUGINS)); 29 } 30 sort($plugin_files); 31 } 32 return $plugin_files; 33 } 34 35 // http://doc.spip.org/@liste_plugin_valides 36 function liste_plugin_valides($liste_plug,&$infos){ 37 $liste = array(); 38 $infos = array(); 39 if (is_array($liste_plug)) 40 foreach($liste_plug as $plug){ 41 $infos[$plug] = plugin_get_infos($plug); 42 if (!isset($infos[$plug]['erreur']) && !isset($plugin_valides[$p=strtoupper($infos[$plug]['prefix'])])) 43 $liste[$p] = array('dir'=>$plug,'version'=>isset($infos[$plug]['version'])?$infos[$plug]['version']:NULL); 44 } 45 return $liste; 46 } 47 48 // A utiliser pour initialiser ma variable globale $plugin 49 // http://doc.spip.org/@liste_plugin_actifs 50 function liste_plugin_actifs(){ 51 $meta_plugin = isset($GLOBALS['meta']['plugin'])?$GLOBALS['meta']['plugin']:''; 52 if (strlen($meta_plugin)>0){ 53 if (is_array($t=unserialize($meta_plugin))) 54 return $t; 55 else{ // compatibilite pre 1.9.2, mettre a jour la meta 56 $t = explode(",",$meta_plugin); 57 $liste = liste_plugin_valides($t,$infos); 58 ecrire_meta('plugin',serialize($liste)); 59 ecrire_metas(); 60 return $liste; 61 } 62 } 63 else 64 return array(); 65 } 66 // http://doc.spip.org/@liste_chemin_plugin_actifs 67 function liste_chemin_plugin_actifs(){ 68 $liste = liste_plugin_actifs(); 69 foreach ($liste as $prefix=>$infos) { 70 $liste[$prefix] = $infos['dir']; 71 } 72 return $liste; 73 } 74 75 // http://doc.spip.org/@ecrire_plugin_actifs 76 function ecrire_plugin_actifs($plugin,$pipe_recherche=false,$operation='raz'){ 77 static $liste_pipe_manquants=array(); 78 $liste_fichier_verif = array(); 79 if (($pipe_recherche)&&(!in_array($pipe_recherche,$liste_pipe_manquants))) 80 $liste_pipe_manquants[]=$pipe_recherche; 81 82 if ($operation!='raz'){ 83 $plugin_actifs = liste_chemin_plugin_actifs(); 84 $plugin_liste = liste_plugin_files(); 85 $plugin_valides = array_intersect($plugin_actifs,$plugin_liste); 86 if ($operation=='ajoute') 87 $plugin = array_merge($plugin_valides,$plugin); 88 if ($operation=='enleve') 89 $plugin = array_diff($plugin_valides,$plugin); 90 } 91 92 $plugin_valides = liste_plugin_valides($plugin,$infos); 93 ecrire_meta('plugin',serialize($plugin_valides)); 94 $plugin_header_info = array(); 95 foreach($plugin_valides as $p=>$info){ 96 $plugin_header_info[]= $p.($info['version']?"(".$info['version'].")":""); 97 } 98 ecrire_meta('plugin_header',substr(strtolower(implode(",",$plugin_header_info)),0,900)); 99 100 $start_file = "<"."?php\nif (!defined('_ECRIRE_INC_VERSION')) return;\n"; 101 $end_file = "\n?".">"; 102 103 // generer les fichier 104 // charger_plugins_options.php 105 // charger_plugins_fonctions.php 106 foreach(array('options','fonctions') as $charge){ 107 $s = ""; 108 $splugs = ""; 109 if (is_array($infos)){ 110 foreach($infos as $plug=>$info){ 111 // definir le plugin, donc le path avant l'include du fichier options 112 // permet de faire des include_ecrire pour attraper un inc_ du plugin 113 if ($charge=='options'){ 114 $prefix = strtoupper($info['prefix']); 115 $splugs .= '$GLOBALS[\'plugins\'][]=\''.$plug.'\';'; 116 $splugs .= "define('_DIR_PLUGIN_$prefix',_DIR_PLUGINS.'$plug/');"; 117 $splugs .= "\n"; 118 } 119 if (isset($info[$charge])){ 120 foreach($info[$charge] as $file){ 121 $s .= "@include_once _DIR_PLUGINS.'$plug/".trim($file)."';\n"; 122 $liste_fichier_verif[] = "_DIR_PLUGINS.'$plug/".trim($file)."'"; 123 } 124 } 125 } 126 } 127 ecrire_fichier(_DIR_TMP."charger_plugins_$charge.php", 128 $start_file . $splugs . $s . $end_file); 129 } 130 131 if (is_array($infos)){ 132 // construire tableaux de pipelines et matrices 133 // $GLOBALS['spip_pipeline'] 134 // $GLOBALS['spip_matrice'] 135 foreach($infos as $plug=>$info){ 136 $prefix = ""; 137 $prefix = $info['prefix']."_"; 138 if (isset($info['pipeline']) AND is_array($info['pipeline'])){ 139 foreach($info['pipeline'] as $pipe){ 140 $nom = trim(array_pop($pipe['nom'])); 141 if (isset($pipe['action'])) 142 $action = trim(array_pop($pipe['action'])); 143 else 144 $action = $nom; 145 if (strpos($GLOBALS['spip_pipeline'][$nom],"|$prefix$action")===FALSE) 146 $GLOBALS['spip_pipeline'][$nom] .= "|$prefix$action"; 147 if (isset($pipe['inclure'])){ 148 $GLOBALS['spip_matrice']["$prefix$action"] = 149 "_DIR_PLUGINS$plug/".array_pop($pipe['inclure']); 150 } 151 } 152 } 153 } 154 } 155 // on ajoute les pipe qui ont ete recenses manquants 156 foreach($liste_pipe_manquants as $add_pipe) 157 if (!isset($GLOBALS['spip_pipeline'][$add_pipe])) 158 $GLOBALS['spip_pipeline'][$add_pipe]= ''; 159 160 $liste_fichier_verif2 = pipeline_precompile(); 161 $liste_fichier_verif = array_merge($liste_fichier_verif,$liste_fichier_verif2); 162 163 // horrible ! 164 foreach ($liste_fichier_verif as $k => $f) 165 $liste_fichier_verif[$k] = _DIR_PLUGINS.preg_replace(",(_DIR_PLUGINS\.)?',", "", $f); 166 ecrire_fichier(_DIR_TMP.'verifier_plugins.txt', 167 serialize($liste_fichier_verif)); 168 } 169 170 // precompilation des pipelines 171 // http://doc.spip.org/@pipeline_precompile 172 function pipeline_precompile(){ 173 global $spip_pipeline, $spip_matrice; 174 $liste_fichier_verif = array(); 175 176 $start_file = "<"."?php\nif (!defined('_ECRIRE_INC_VERSION')) return;\n"; 177 $end_file = "\n?".">"; 178 $content = ""; 179 foreach($spip_pipeline as $action=>$pipeline){ 180 $s_inc = ""; 181 $s_call = ""; 182 $pipe = array_filter(explode('|',$pipeline)); 183 // Eclater le pipeline en filtres et appliquer chaque filtre 184 foreach ($pipe as $fonc) { 185 $s_call .= '$val = minipipe(\''.$fonc.'\', $val);'."\n"; 186 if (isset($spip_matrice[$fonc])){ 187 $file = $spip_matrice[$fonc]; 188 $s_inc .= '@include_once('; 189 // si _DIR_PLUGINS est dans la chaine, on extrait la constante 190 if (($p = strpos($file,'_DIR_PLUGINS'))!==FALSE){ 191 $f = ""; 192 if ($p) 193 $f .= "'".substr($file,0,$p)."'."; 194 $f .= "_DIR_PLUGINS."; 195 $f .= "'".substr($file,$p+12)."'"; 196 $s_inc .= $f; 197 $liste_fichier_verif[] = $f; 198 } 199 else{ 200 $s_inc .= "'$file'"; 201 $liste_fichier_verif[] = "'$file'"; 202 } 203 $s_inc .= ');'."\n"; 204 } 205 } 206 $content .= "// Pipeline $action \n"; 207 $content .= "function execute_pipeline_$action(\$val){\n"; 208 $content .= $s_inc; 209 $content .= $s_call; 210 $content .= "return \$val;\n}\n\n"; 211 } 212 ecrire_fichier(_DIR_TMP."charger_pipelines.php", 213 $start_file . $content . $end_file); 214 return $liste_fichier_verif; 215 } 216 217 // pas sur que ca serve... 218 // http://doc.spip.org/@liste_plugin_inactifs 219 function liste_plugin_inactifs(){ 220 return array_diff (liste_plugin_files(),liste_chemin_plugin_actifs()); 221 } 222 223 // mise a jour du meta en fonction de l'etat du repertoire 224 // penser a faire une maj du cache => ecrire_meta() 225 // en principe cela doit aussi initialiser la valeur a vide si elle n'esite pas 226 // risque de pb en php5 a cause du typage ou de null (verifier dans la doc php) 227 // http://doc.spip.org/@verif_plugin 228 function verif_plugin($pipe_recherche = false){ 229 $plugin_actifs = liste_chemin_plugin_actifs(); 230 $plugin_liste = liste_plugin_files(); 231 $plugin_new = array_intersect($plugin_actifs,$plugin_liste); 232 ecrire_plugin_actifs($plugin_new,$pipe_recherche); 233 ecrire_metas(); 234 } 235 236 // http://doc.spip.org/@ordonne_plugin 237 function ordonne_plugin(){ 238 $liste = liste_chemin_plugin_actifs(); 239 $liste_triee = array(); 240 $i=2; 241 foreach($liste as $plug){ 242 $index = $i; 243 $i = $i+2; 244 if (rawurldecode($_GET['monter'])==$plug) $index = $index-3; 245 if (rawurldecode($_GET['descendre'])==$plug) $index = $index+3; 246 $liste_triee[$index] = $plug; 247 } 248 ksort($liste_triee); 249 ecrire_plugin_actifs($liste_triee); 250 ecrire_metas(); 251 } 252 253 // http://doc.spip.org/@spip_plugin_install 254 function spip_plugin_install($action,$prefix,$version_cible){ 255 $nom_meta_base_version = $prefix."_base_version"; 256 switch ($action){ 257 case 'test': 258 return (isset($GLOBALS['meta'][$nom_meta_base_version]) AND ($GLOBALS['meta'][$nom_meta_base_version]>=$version_cible)); 259 break; 260 case 'install': 261 if (function_exists($upgrade = $prefix."_upgrade")) 262 $upgrade($nom_meta_base_version,$version_cible); 263 break; 264 case 'uninstall': 265 if (function_exists($vider_tables = $prefix."_vider_tables")) 266 $vider_tables($nom_meta_base_version); 267 break; 268 } 269 } 270 271 // http://doc.spip.org/@desinstalle_un_plugin 272 function desinstalle_un_plugin($plug,$infos){ 273 // faire les include qui vont bien 274 foreach($infos['install'] as $file){ 275 $file = trim($file); 276 @include_once(_DIR_PLUGINS."$plug/$file"); 277 } 278 $prefix_install = $infos['prefix']."_install"; 279 if (function_exists($prefix_install)){ 280 $prefix_install('uninstall'); 281 $ok = $prefix_install('test'); 282 return $ok; 283 } 284 if (isset($infos['version_base'])){ 285 spip_plugin_install('uninstall',$infos['prefix'],$infos['version_base']); 286 $ok = spip_plugin_install('test',$infos['prefix'],$infos['version_base']); 287 return $ok; 288 } 289 290 return false; 291 } 292 293 // http://doc.spip.org/@installe_un_plugin 294 function installe_un_plugin($plug,$infos){ 295 // faire les include qui vont bien 296 foreach($infos['install'] as $file){ 297 $file = trim($file); 298 @include_once(_DIR_PLUGINS."$plug/$file"); 299 } 300 $prefix_install = $infos['prefix']."_install"; 301 // cas de la fonction install fournie par le plugin 302 if (function_exists($prefix_install)){ 303 // voir si on a besoin de faire l'install 304 $ok = $prefix_install('test'); 305 if (!$ok) { 306 $prefix_install('install'); 307 $ok = $prefix_install('test'); 308 } 309 return $ok; // le plugin est deja installe et ok 310 } 311 // pas de fonction instal fournie, mais une version_base dans le plugin 312 // on utilise la fonction par defaut 313 if (isset($infos['version_base'])){ 314 $ok = spip_plugin_install('test',$infos['prefix'],$infos['version_base']); 315 if (!$ok) { 316 spip_plugin_install('install',$infos['prefix'],$infos['version_base']); 317 $ok = spip_plugin_install('test',$infos['prefix'],$infos['version_base']); 318 } 319 return $ok; // le plugin est deja installe et ok 320 } 321 return false; 322 } 323 324 // http://doc.spip.org/@installe_plugins 325 function installe_plugins(){ 326 $meta_plug_installes = array(); 327 $liste = liste_chemin_plugin_actifs(); 328 foreach($liste as $plug){ 329 $infos = plugin_get_infos($plug); 330 if (isset($infos['install'])){ 331 $ok = installe_un_plugin($plug,$infos); 332 // on peut enregistrer le chemin ici car il est mis a jour juste avant l'affichage 333 // du panneau -> cela suivra si le plugin demenage 334 if ($ok) 335 $meta_plug_installes[] = $plug; 336 } 337 } 338 ecrire_meta('plugin_installes',serialize($meta_plug_installes),'non'); 339 ecrire_metas(); 340 } 341 // http://doc.spip.org/@plugin_est_installe 342 function plugin_est_installe($plug_path){ 343 $plugin_installes = isset($GLOBALS['meta']['plugin_installes'])?unserialize($GLOBALS['meta']['plugin_installes']):array(); 344 if (!$plugin_installes) return false; 345 return in_array($plug_path,$plugin_installes); 346 } 347 348 // lecture du fichier de configuration d'un plugin 349 // http://doc.spip.org/@plugin_get_infos 350 function plugin_get_infos($plug){ 351 include_spip('inc/xml'); 352 static $infos=array(); 353 static $plugin_xml_cache=NULL; 354 if (!isset($infos[$plug])){ 355 if ($plugin_xml_cache==NULL){ 356 $plugin_xml_cache = array(); 357 if (is_file($f=_DIR_TMP."plugin_xml.cache")){ 358 lire_fichier($f,$contenu); 359 $plugin_xml_cache = unserialize($contenu); 360 if (!is_array($plugin_xml_cache)) $plugin_xml_cache = array(); 361 } 362 } 363 $ret = array(); 364 if (isset($plugin_xml_cache[$plug])){ 365 $info = $plugin_xml_cache[$plug]; 366 if (isset($info['filemtime']) && (@filemtime(_DIR_PLUGINS."$plug/plugin.xml")<=$info['filemtime'])) 367 $ret = $info; 368 } 369 if (!count($ret)){ 370 if ((@file_exists(_DIR_PLUGINS))&&(is_dir(_DIR_PLUGINS))){ 371 if (@file_exists($f = _DIR_PLUGINS."$plug/plugin.xml")) { 372 $arbre = spip_xml_load($f); 373 if (!$arbre OR !isset($arbre['plugin']) OR !is_array($arbre['plugin'])) 374 $arbre = array('erreur' => array(_T('erreur_plugin_fichier_def_incorrect')." : $plug/plugin.xml")); 375 } 376 else { 377 // pour arriver ici on l'a vraiment cherche... 378 $arbre = array('erreur' => array(_T('erreur_plugin_fichier_def_absent')." : $plug/plugin.xml")); 379 } 380 plugin_verifie_conformite($plug,$arbre); 381 $ret['nom'] = spip_xml_aplatit($arbre['nom']); 382 $ret['version'] = trim(end($arbre['version'])); 383 if (isset($arbre['auteur'])) 384 $ret['auteur'] = spip_xml_aplatit($arbre['auteur']); 385 if (isset($arbre['icon'])) 386 $ret['icon'] = spip_xml_aplatit($arbre['icon']); 387 if (isset($arbre['description'])) 388 $ret['description'] = spip_xml_aplatit($arbre['description']); 389 if (isset($arbre['lien'])) 390 $ret['lien'] = join(' ',$arbre['lien']); 391 if (isset($arbre['etat'])) 392 $ret['etat'] = trim(end($arbre['etat'])); 393 if (isset($arbre['options'])) 394 $ret['options'] = $arbre['options']; 395 if (isset($arbre['install'])) 396 $ret['install'] = $arbre['install']; 397 if (isset($arbre['fonctions'])) 398 $ret['fonctions'] = $arbre['fonctions']; 399 $ret['prefix'] = trim(array_pop($arbre['prefix'])); 400 if (isset($arbre['pipeline'])) 401 $ret['pipeline'] = $arbre['pipeline']; 402 if (isset($arbre['erreur'])) 403 $ret['erreur'] = $arbre['erreur']; 404 if (isset($arbre['version_base'])) 405 $ret['version_base'] = trim(end($arbre['version_base'])); 406 407 if ($t=@filemtime($f)){ 408 $ret['filemtime'] = $t; 409 $plugin_xml_cache[$plug]=$ret; 410 ecrire_fichier(_DIR_TMP."plugin_xml.cache",serialize($plugin_xml_cache)); 411 } 412 } 413 } 414 $infos[$plug] = $ret; 415 } 416 return $infos[$plug]; 417 } 418 419 // http://doc.spip.org/@plugin_verifie_conformite 420 function plugin_verifie_conformite($plug,&$arbre){ 421 $silence = false; 422 if (isset($arbre['plugin'])&&is_array($arbre['plugin'])) 423 $arbre = end($arbre['plugin']); // derniere def plugin 424 else{ 425 $arbre = array('erreur' => array(_T('erreur_plugin_tag_plugin_absent')." : $plug/plugin.xml")); 426 $silence = true; 427 } 428 // verification de la conformite du plugin avec quelques 429 // precautions elementaires 430 if (!isset($arbre['nom'])){ 431 if (!$silence) 432 $arbre['erreur'][] = _T('erreur_plugin_nom_manquant'); 433 $arbre['nom'] = array(""); 434 } 435 if (!isset($arbre['version'])){ 436 if (!$silence) 437 $arbre['erreur'][] = _T('erreur_plugin_version_manquant'); 438 $arbre['version'] = array(""); 439 } 440 if (!isset($arbre['prefix'])){ 441 if (!$silence) 442 $arbre['erreur'][] = _T('erreur_plugin_prefix_manquant'); 443 $arbre['prefix'] = array(""); 444 } 445 else{ 446 $prefix = ""; 447 $prefix = trim(end($arbre['prefix'])); 448 if (isset($arbre['etat'])){ 449 $etat = trim(end($arbre['etat'])); 450 if (!in_array($etat,array('dev','experimental','test','stable'))) 451 $arbre['erreur'][] = _T('erreur_plugin_etat_inconnu')." : $etat"; 452 } 453 if (isset($arbre['options'])){ 454 foreach($arbre['options'] as $optfile){ 455 $optfile = trim($optfile); 456 if (!@is_readable(_DIR_PLUGINS."$plug/$optfile")) 457 if (!$silence) 458 $arbre['erreur'][] = _T('erreur_plugin_fichier_absent')." : $optfile"; 459 } 460 } 461 if (isset($arbre['fonctions'])){ 462 foreach($arbre['fonctions'] as $optfile){ 463 $optfile = trim($optfile); 464 if (!@is_readable(_DIR_PLUGINS."$plug/$optfile")) 465 if (!$silence) 466 $arbre['erreur'][] = _T('erreur_plugin_fichier_absent')." : $optfile"; 467 } 468 } 469 $fonctions = array(); 470 if (isset($arbre['fonctions'])) 471 $fonctions = $arbre['fonctions']; 472 $liste_methodes_reservees = array('__construct','__destruct','plugin','install','uninstall',strtolower($prefix)); 473 if (is_array($arbre['pipeline'])){ 474 foreach($arbre['pipeline'] as $pipe){ 475 $nom = trim(end($pipe['nom'])); 476 if (isset($pipe['action'])) 477 $action = trim(end($pipe['action'])); 478 else 479 $action = $nom; 480 // verif que la methode a un nom autorise 481 if (in_array(strtolower($action),$liste_methodes_reservees)){ 482 if (!$silence) 483 $arbre['erreur'][] = _T("erreur_plugin_nom_fonction_interdit")." : $action"; 484 } 485 else{ 486 // verif que le fichier de def est bien present 487 if (isset($pipe['inclure'])){ 488 $inclure = _DIR_PLUGINS."$plug/".end($pipe['inclure']); 489 if (!@is_readable($inclure)) 490 if (!$silence) 491 $arbre['erreur'][] = _T('erreur_plugin_fichier_absent')." : $inclure"; 492 } 493 } 494 } 495 } 496 } 497 } 498 499 // http://doc.spip.org/@verifie_include_plugins 500 function verifie_include_plugins() { 501 if (_request('exec')!="admin_plugin" 502 AND $_SERVER['X-Requested-With'] != 'XMLHttpRequest'){ 503 if (@is_readable(_DIR_PLUGINS)) { 504 include_spip('inc/headers'); 505 redirige_par_entete(generer_url_ecrire("admin_plugin")); 506 } 507 // plus de repertoire plugin existant, le menu n'existe plus 508 // on fait une mise a jour silencieuse 509 // generer les fichiers php precompiles 510 // de chargement des plugins et des pipelines 511 verif_plugin(); 512 spip_log("desactivation des plugins suite a suppression du repertoire"); 513 } 514 } 515 516 // http://doc.spip.org/@affiche_bloc_plugin 517 function affiche_bloc_plugin($plug_file, $info) { 518 global $spip_lang_right; 519 520 // puce d'etat du plugin 521 // <etat>dev|experimental|test|stable</etat> 522 $etat = 'dev'; 523 if (isset($info['etat'])) 524 $etat = $info['etat']; 525 switch ($etat) { 526 case 'experimental': 527 $puce = 'puce-rouge.gif'; 528 $titre_etat = _T('plugin_etat_experimental'); 529 break; 530 case 'test': 531 $puce = 'puce-orange.gif'; 532 $titre_etat = _T('plugin_etat_test'); 533 break; 534 case 'stable': 535 $puce = 'puce-verte.gif'; 536 $titre_etat = _T('plugin_etat_stable'); 537 break; 538 default: 539 $puce = 'puce-poubelle.gif'; 540 $titre_etat = _T('plugin_etat_developpement'); 541 break; 542 } 543 544 $s .= "<div class='detailplugin verdana2'>"; 545 546 if (isset($info['icon'])) 547 $s .= "<img src='". _DIR_PLUGINS.$plug_file.'/'.trim($info['icon'])."' style='float:$spip_lang_right;' alt=' ' />\n"; 548 549 $s .= _T('version') .' '. $info['version'] . " | <strong>$titre_etat</strong><br/>"; 550 $s .= _T('repertoire_plugins') .' '. $plug_file . "<br/>"; 551 552 if (isset($info['description'])) 553 $s .= "<hr/>" . propre($info['description']) . "<br/>"; 554 555 if (isset($info['auteur'])) 556 $s .= "<hr/>" . _T('auteur') .' '. propre($info['auteur']) . "<br/>"; 557 558 if (trim($info['lien'])) { 559 if (preg_match(',^https?://,iS', $info['lien'])) 560 $s .= "<hr/>" . _T('info_url') .' '. propre("[->".$info['lien']."]") . "<br/>"; 561 else 562 $s .= "<hr/>" . _T('info_url') .' '. propre($info['lien']) . "<br/>"; 563 } 564 $s .= "</div>"; 565 566 return $s; 567 } 568 569 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Wed Nov 21 10:20:27 2007 | par Balluche grâce à PHPXref 0.7 |
![]() |