| [ Index ] |
|
Code source de Phorum 5.1.25 |
1 <?php 2 3 //////////////////////////////////////////////////////////////////////////////// 4 // // 5 // Copyright (C) 2006 Phorum Development Team // 6 // http://www.phorum.org // 7 // // 8 // This program is free software. You can redistribute it and/or modify // 9 // it under the terms of either the current Phorum License (viewable at // 10 // phorum.org) or the Phorum License that was distributed with this file // 11 // // 12 // This program is distributed in the hope that it will be useful, // 13 // but WITHOUT ANY WARRANTY, without even the implied warranty of // 14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // 15 // // 16 // You should have received a copy of the Phorum License // 17 // along with this program. // 18 //////////////////////////////////////////////////////////////////////////////// 19 20 // TODO have a better way to differentiate between Phorum distribution 21 // TODO and addon files, so we son't index text strings from addon 22 // TODO files in here. 23 24 if(!defined("PHORUM_ADMIN")) return; 25 26 define('TOKEN_DEBUGGER', 0); 27 28 // Because sometimes the script can take a while, we set the 29 // PHP time limit to a high value to prevent execution timeouts. 30 set_time_limit(600); 31 32 include_once "./include/admin/PhorumInputForm.php"; 33 34 // Get some form variables. 35 $action = isset($_POST['action']) ? $_POST['action'] : 'start'; 36 $language = isset($_POST['language']) ? $_POST['language'] : $PHORUM["SETTINGS"]["default_language"]; 37 $filename = isset($_POST['filename']) ? trim($_POST['filename']) : ''; 38 $displayname = isset($_POST['displayname']) ? trim($_POST['displayname']) : ''; 39 40 // Handle downloading a new language file. 41 if ($action == 'download_lang') 42 { 43 // Ditch HTML header we have so far (from the admin framework). 44 ob_end_clean(); 45 46 // Send the new languagefile to the client. 47 $basename = preg_replace('/-.*$/', '', $filename); 48 $fullfile = $basename . '-' . PHORUM . '.php'; 49 header ("Content-Type: application/download; filename=$fullfile"); 50 header ("Content-Disposition: attachment; filename=\"$fullfile\""); 51 $langfile = phorum_cache_get('updated_language', $filename); 52 print $langfile; 53 54 exit(); 55 } 56 57 // Handle updating a language. 58 if ($action == 'update_lang') { 59 $langinfo = phorum_get_language_info(); 60 return phorum_generate_language_file($language, $langinfo[$language], false); 61 } 62 63 // Handle generating a new language. 64 if ($action == 'generate_lang') { 65 $filename = preg_replace('/\.php$/i', '', basename($filename)); 66 if ($filename == '') { 67 phorum_admin_error("The basename may not be empty"); 68 } elseif (! preg_match('/^[\w_\.]+$/', $filename)) { 69 phorum_admin_error( 70 "The basename contains illegal characters. Please, keep the " . 71 "filename simple by using only letters, numbers, underscores and " . 72 "dots. You can't use hyphens, because those are used for " . 73 "separating the basename from the Phorum version for which the " . 74 "language file is used." 75 ); 76 } elseif ($displayname == '') { 77 phorum_admin_error("The display name for the language may not be empty."); 78 } else { 79 $filename .= "-" . PHORUM; 80 return phorum_generate_language_file($filename, $displayname, true); 81 } 82 } 83 84 85 // Handle start page. 86 $frm = new PhorumInputForm ("", "post", "Generate updated language file"); 87 88 $frm->addmessage(<<<INTRO 89 <font color="red">EXPERIMENTAL FEATURE<br/> 90 Please backup your existing language file if you replace it with 91 one generated by this maintenance tool. We feel pretty confident 92 about it, but we wouldn't want you to loose data in case of bugs.</font> 93 <hr size="0"/> 94 95 <h2>Manage language files</h2> 96 This is a tool which can be used for easy maintenance of 97 language files for Phorum. It will collect all actual used 98 language strings from the Phorum software and generate an 99 updated langage file for your language of choice based on 100 those strings. In the generated language file, missing and 101 deprecated strings will be clearly marked, so you can 102 update the language file to match the running Phorum distribution. 103 INTRO 104 ); 105 106 $frm->hidden("module", "manage_languages"); 107 $frm->hidden("action", "update_lang"); 108 $frm->addbreak("Update existing language file"); 109 $frm->addrow("Generate updated version of an existing language file", 110 $frm->select_tag("language", phorum_get_language_info(), $language, 0)); 111 $frm->show(); 112 113 114 $frm = new PhorumInputForm("", "post", "Generate new language file"); 115 $frm->addmessage(<<<INTRO 116 In case there is no language file available for your language or 117 if you want to create a new language file all of your own, you can 118 generate a new language file using the form below. 119 INTRO 120 ); 121 122 $frm->hidden("module", "manage_languages"); 123 $frm->hidden("action", "generate_lang"); 124 $frm->addbreak("Generate a new language file"); 125 $frm->addrow("The basename for the generated file", $frm->text_box('filename', $filename, 20)); 126 $frm->addrow("The display name for the language", $frm->text_box('displayname', $displayname, 20)); 127 $frm->show(); 128 129 exit; 130 131 132 // ====================================================================== 133 // Generating language files 134 // ====================================================================== 135 136 function phorum_generate_language_file($lang, $displayname, $generate_new) 137 { 138 global $fullfile; 139 140 $basename = preg_replace('/-.*$/', '', $lang); 141 $fullfile = $basename . '-' . PHORUM . '.php'; 142 143 // Get our default language file. 144 $DEFAULT = phorum_get_language('english'); 145 146 // Get the languagefile to update, unless generating a new language. 147 $CURRENT = array(); 148 if (! $generate_new) { 149 $CURRENT = phorum_get_language($lang); 150 } else { 151 $CURRENT['STORE']['language_hide'] = 0; 152 $CURRENT['STORE']['language'] = urlencode("'" . addslashes($displayname) . "'"); 153 } 154 155 // Keep a copy of the languagefile. 156 $CURRENT_COPY = $CURRENT; 157 158 // Collect all language strings from the distribution files. 159 $language_strings = phorum_extract_language_strings(); 160 161 $frm = new PhorumInputForm ("", "post", "Download new " . htmlspecialchars($fullfile) . " language file"); 162 $frm->hidden("module", "manage_languages"); 163 $frm->hidden("action", "download_lang"); 164 $frm->hidden("filename", $lang); 165 166 if (! $generate_new) { 167 168 $frm->addmessage( 169 "<h2>Update language: " . htmlspecialchars($displayname) . "</h2>" . 170 "Below you will see all the things that have been updated " . 171 "to get to the new version of the language file. At the " . 172 "bottom of the page you will find a download button to download " . 173 "the updated language file. This language file has to be placed " . 174 "in <b>include/lang/" . htmlspecialchars($lang) . ".php</b> to make it " . 175 "available to Phorum (backup your old file first of course!). " . 176 "If new language strings have been added, " . 177 "they will be marked with '***' in the language file, so it's " . 178 "easy for you to find them." 179 ); 180 $frm->addbreak("Updates for the new language file"); 181 } else { 182 $frm->addmessage( 183 "<h2>Generate new language: " . htmlspecialchars($displayname) . "</h2>" . 184 "A new language file has been generated. Below you will find " . 185 "a download button to download the new file. In this file, you " . 186 "can replace all language strings by strings which apply to " . 187 "\"" . htmlspecialchars($displayname) . "\". After updating the new " . 188 "file, you will have to place it in " . 189 "<b>include/lang/" . htmlspecialchars($fullfile) . ".php</b>, " . 190 "so Phorum can use it (backup your old file first of course!)." 191 ); 192 } 193 194 $notifies = 0; 195 196 // Check for language strings that are missing. 197 $missing = array(); 198 $count_missing = 0; 199 foreach ($language_strings as $string => $data) { 200 if ($string == 'TIME') continue; // This one is special. 201 if (! isset($CURRENT["DATA"]["LANG"][$string])) { 202 array_push($missing, $string); 203 $translation = urlencode("'" . addslashes($string) . "'"); 204 if (isset($DEFAULT["DATA"]["LANG"][$string])) { 205 $translation = $DEFAULT["DATA"]["LANG"][$string]; 206 } 207 $CURRENT_COPY["DATA"]["LANG"][$string] = 208 urlencode("'***'. " . urldecode($translation)); 209 210 $count_missing++; 211 if (! $generate_new) { 212 $frm->addrow("MISSING ($count_missing)", $string); 213 $notifies++; 214 } 215 } else { 216 unset($CURRENT["DATA"]["LANG"][$string]); 217 } 218 } 219 220 // Check for language strings that are deprecated. 221 $deprecated = array(); 222 $count_deprecated = 0; 223 if (! $generate_new) 224 { 225 foreach ($CURRENT["DATA"]["LANG"] as $string => $translation) 226 { 227 if ($string == 'TIME') continue; // This one is special. 228 229 $count_deprecated++; 230 $deprecated[$string] = true; 231 232 // Only notify the deprecation if not already in deprecated state. 233 if (! isset($CURRENT['STORE']['DEPRECATED'][$string])) { 234 $frm->addrow("DEPRECATED ($count_deprecated)", htmlspecialchars($string)); 235 $notifies++; 236 } 237 } 238 } 239 $CURRENT_COPY['STORE']['DEPRECATED'] = $deprecated; 240 241 // Restore our full current language data from the copy. 242 $CURRENT = $CURRENT_COPY; 243 244 // Copy values from our default language to the current language. 245 $copyfields = array('long_date', 'short_date', 'locale'); 246 foreach ($copyfields as $f) { 247 if (! isset($CURRENT[$f])) { 248 $CURRENT[$f] = $DEFAULT[$f]; 249 if (! $generate_new) { 250 $frm->addrow("MISSING VARIABLE", "$f set to default " . 251 htmlentities(urldecode($DEFAULT[$f]))); 252 $notifies++; 253 } 254 } 255 } 256 // Copy default values beneath DATA to the current language. 257 $datafields = array('CHARSET', 'MAILENCODING', 'LANG_META'); 258 foreach ($datafields as $f) { 259 if (! isset($CURRENT['DATA'][$f]) || $CURRENT['DATA'][$f] == '') { 260 $CURRENT['DATA'][$f] = $DEFAULT['DATA'][$f]; 261 if (! $generate_new) { 262 $frm->addrow("MISSING VARIABLE", "DATA->$f set to default " . 263 htmlentities(urldecode($DEFAULT['DATA'][$f]))); 264 $notifies++; 265 } 266 } 267 } 268 269 // Copy default values for timezone information to the current language. 270 foreach ($DEFAULT['DATA']['LANG']['TIME'] as $key => $val) { 271 if (! isset($CURRENT['DATA']['LANG']['TIME'][$key])) { 272 $CURRENT['DATA']['LANG']['TIME'][$key] = $val; 273 274 if (! $generate_new) { 275 $dflt = htmlentities(urldecode($DEFAULT['DATA']['LANG']['TIME'][$key])); 276 $frm->addrow("MISSING TZINFO", "TZ $key set to default<br/>$dflt"); 277 $notifies++; 278 } 279 } 280 } 281 282 if ($generate_new) { 283 $frm->addrow("COMPLETED", "A new language file has been generated for you"); 284 } elseif (! $notifies) { 285 $frm->addrow("NONE", "There were no updates for the current \"$lang\" language file"); 286 } 287 288 $frm->show(); 289 290 phorum_write_language_file($lang, $CURRENT); 291 } 292 293 function phorum_write_language_file($lang, $CURRENT) 294 { 295 // Sort array keys. 296 ksort($CURRENT['DATA']['LANG']); 297 ksort($CURRENT['STORE']['DEPRECATED']); 298 299 $langfile = 300 "<?php\n" . 301 "\n" . 302 $CURRENT['STORE']['keep_comment'] . "\n" . 303 "\n" . 304 "// ============================================================\n" . 305 "// General settings\n" . 306 "// ============================================================\n" . 307 "\n" . 308 "// The language name as it is presented in the interface.\n" . 309 "\$language = " . urldecode($CURRENT['STORE']['language']) . ";\n" . 310 "\n" . 311 "// Uncomment this to hide this language from the user-select-box.\n" . 312 ($CURRENT['STORE']['language_hide'] ? '' : '//') . "\$language_hide = 1;\n" . 313 "\n" . 314 "// Date formatting. Check the PHP-docs for the syntax of these\n" . 315 "// entries (http://www.php.net/strftime). One tip: do not use\n" . 316 "// %T for showing the time zone, as users can change their time zone.\n" . 317 "\$PHORUM['long_date'] = " . urldecode($CURRENT['long_date']) . ";\n" . 318 "\$PHORUM['short_date'] = " . urldecode($CURRENT['short_date']) . ";\n" . 319 "\n" . 320 "// The locale setting for enabling localized times/dates. Take a look\n" . 321 "// at http://www.w3.org/WAI/ER/IG/ert/iso639.htm for the needed string.\n" . 322 "\$PHORUM['locale'] = " . urldecode($CURRENT['locale']) . ";\n" . 323 "\n" . 324 "// The character set to use for converting html into safe valid text.\n" . 325 "// Also used in the header template for the xml tag. For a list of\n" . 326 "// supported character sets see: http://www.php.net/htmlentities\n" . 327 "// You may also need to set a meta-tag with a character set in it.\n" . 328 "\$PHORUM['DATA']['CHARSET'] = " . urldecode($CURRENT['DATA']['CHARSET']) . ";\n" . 329 "\n" . 330 "// The encoding used for outgoing mail messages.\n" . 331 "\$PHORUM['DATA']['MAILENCODING'] = " . urldecode($CURRENT['DATA']['MAILENCODING']) . ";\n" . 332 "\n" . 333 "// Some languages need additional meta tags to set encoding, etc.\n" . 334 "\$PHORUM['DATA']['LANG_META'] = " . urldecode($CURRENT['DATA']['LANG_META']) . ";\n" . 335 "\n" . 336 "// ============================================================\n" . 337 "// Language translation strings\n" . 338 "// ============================================================\n" . 339 "\n" . 340 "\$PHORUM['DATA']['LANG'] = array(\n"; 341 342 // Add active language data to the array. 343 foreach ($CURRENT['DATA']['LANG'] as $key => $val) { 344 if ($key == 'TIME') continue; 345 if (isset($CURRENT['STORE']['DEPRECATED'][$key])) continue; 346 347 $langfile .= " '$key' => " . urldecode($val) . ",\n"; 348 } 349 350 // Add deprecated language data to the array. 351 if (count($CURRENT['STORE']['DEPRECATED'])) 352 { 353 $langfile .= 354 "\n" . 355 " // ============================================================\n" . 356 " // DEPRECATED:\n" . 357 " // These are all language strings which are not used anymore.\n" . 358 " // You might want to keep them to make this language file work\n" . 359 " // for versions of Phorum prior to version " . PHORUM . "\n" . 360 " // ============================================================\n" . 361 "\n"; 362 363 foreach ($CURRENT['STORE']['DEPRECATED'] as $key => $dummy) { 364 $langfile .= " '$key' => " . urldecode($CURRENT['DATA']['LANG'][$key]) . ",\n"; 365 } 366 } 367 368 $langfile .= 369 ");\n" . 370 "\n" . 371 "// ============================================================\n" . 372 "// Timezone description strings\n" . 373 "// ============================================================\n" . 374 "\n" . 375 "\$PHORUM['DATA']['LANG']['TIME'] = array(\n"; 376 377 foreach ($CURRENT['DATA']['LANG']['TIME'] as $key => $val) { 378 $pre = sprintf(" %6s", "'$key'"); 379 $langfile .= "$pre => " . urldecode($val) . ",\n"; 380 } 381 382 $langfile .= 383 ");\n" . 384 "\n" . 385 "?>\n"; 386 387 phorum_cache_put('updated_language', $lang, $langfile); 388 } 389 390 391 // ====================================================================== 392 // Parsing language files 393 // ====================================================================== 394 395 // Helper function for phorum_get_language() to be able to do 396 // some debugging output while getting all PHP tokens. 397 function token_shift(&$tokens) 398 { 399 $token = array_shift($tokens); 400 if (TOKEN_DEBUGGER > 1) { 401 print '<div style="color: darkorange">'; 402 if (is_array($token)) { 403 print "COMPLEX: " . token_name($token[0]) . " [" . htmlspecialchars($token[1]) . "]<br/>"; 404 } else { 405 print "SIMPLE: [" . htmlspecialchars($token) . "]<br/>"; 406 } 407 print '</div>'; 408 } 409 return $token; 410 } 411 412 function token_skip_whitespace(&$tokens) 413 { 414 while ($tokens[0][0] == T_WHITESPACE) { 415 array_shift($tokens); 416 417 } 418 } 419 420 function token_get_string(&$tokens, $string = NULL) 421 { 422 $levels = 0; 423 424 while (count($tokens)) 425 { 426 $token = token_shift($tokens); 427 428 if (is_array($token)) 429 { 430 switch ($token[0]) 431 { 432 case T_COMMENT: 433 if (strstr($token[1], 'DEPRECATED')) { 434 global $in_deprecated; 435 $in_deprecated = true; 436 } 437 break; 438 439 // Tokens which we handle in scalar token code. 440 case T_DOUBLE_ARROW: 441 $token = '=>'; 442 break; 443 case T_CURLY_OPEN: 444 $token = '{'; 445 break; 446 447 case T_WHITESPACE: 448 case T_ENCAPSED_AND_WHITESPACE: 449 case T_CONSTANT_ENCAPSED_STRING: 450 case T_NUM_STRING: 451 case T_STRING: 452 case T_ARRAY: 453 case T_LNUMBER: 454 case T_VARIABLE: 455 case T_CHARACTER: 456 $string .= $token[1]; 457 break; 458 default: 459 die ("Unhandled complex " . token_name($token[0]) . " token in token_get_string: " . 460 htmlspecialchars($token[1])); 461 break; 462 } 463 } 464 465 if (is_scalar($token)) 466 { 467 $oldlevels = $levels; 468 469 // Keep track of nested brackets and curlies. 470 if ($token == '(' || $token == '{' || $token == '[') { 471 $levels++; 472 } elseif ($levels && ($token == ')' || $token == '}' || $token == ']')) { 473 $levels--; 474 } 475 476 if ($levels || $oldlevels) { 477 $string .= $token; 478 } else { 479 // Tokens which end a string. 480 if ($token == ';' || $token == '=' || 481 $token == '=>' || $token == ',' || 482 $token == ')') { 483 $string = trim($string); 484 return array($string, $token); 485 } else { 486 $string .= $token; 487 } 488 } 489 } 490 } 491 } 492 493 // This function retrieves all info from a language file, by directly 494 // parsing its tokens. We can't simply load the language file, because 495 // we have to extract any PHP code intact from it. By loading, all 496 // PHP code would be interpreted. 497 function phorum_get_language($lang) 498 { 499 $path = "./include/lang/$lang.php"; 500 $PHORUM = array(); 501 $DEPRECATED = array(); 502 $keep_comment = ''; 503 if (! file_exists($path)) { 504 die("Cannot locate language module in $path"); 505 } 506 507 // Read the language file. Keep track of comments that 508 // we want to keep (those starting with '##'). 509 $file = ''; 510 $fp = fopen($path, "r"); 511 if (! $fp) die("Cannot read language file $path"); 512 while (($line = fgets($fp))) { 513 $file .= $line; 514 if (substr($line, 0, 2) == '##') { 515 $keep_comment .= $line; 516 } 517 } 518 fclose($fp); 519 520 // Split the contents of the language file into PHP tokens. 521 $tokens = token_get_all($file); 522 523 // Parse the PHP tokens. 524 while (count($tokens)) 525 { 526 // Extract all variables. The rest is ignored. 527 $token = token_shift($tokens); 528 if (is_array($token)) 529 { 530 if ($token[0] == T_VARIABLE) { 531 list($varname,$endedby) = token_get_string($tokens, $token[1]); 532 if ($endedby != '=') break; // We want only the assignments. 533 534 // Peek at the following code, to see what type of variable we're 535 // handling. Scalar or array. 536 token_skip_whitespace($tokens); 537 if ($tokens[0][0] == T_ARRAY) 538 { 539 global $in_deprecated; 540 $in_deprecated = false; 541 542 // Handle opening bracket for the array. 543 token_shift($tokens); 544 token_skip_whitespace($tokens); 545 $token = token_shift($tokens); 546 if ($token != '(') { 547 die("$path: Expected array opening bracket for array " . 548 htmlspecialchars($varname)); 549 } 550 551 while (count($tokens)) 552 { 553 // Get key 554 list($key, $endedby) = token_get_string($tokens); 555 if ($endedby != '=>') { 556 die("$path: Expected double arrow (=>) for key " . 557 htmlspecialchars($key) . " in array " . 558 htmlspecialchars($varname) . ", but got $endedby"); 559 } 560 561 // Get value 562 list($val, $endedby) = token_get_string($tokens); 563 564 if ($endedby != ',' && $endedby != ')') { 565 die("$path: Expected ending comma or bracket for key " . 566 htmlspecialchars($key) . " in array " . 567 htmlspecialchars($varname) . ", but got $endedby"); 568 } 569 570 // Put the data in the environment. 571 $fullvar = $varname . '[' . $key . ']'; 572 eval("$fullvar = '" . urlencode($val) . "';"); 573 574 // Keep track of data flagged deprecated. 575 if ($in_deprecated) { 576 eval("\$DEPRECATED[$key] = true;"); 577 } 578 579 // Last key/value pair? 580 if ($endedby == ')') break; 581 token_skip_whitespace($tokens); 582 if ($tokens[0] == ')') { 583 array_shift($tokens); 584 break; 585 } 586 } 587 } else { 588 list($varvalue,$endedby) = token_get_string($tokens); 589 eval("$varname = '" . urlencode($varvalue) . "';"); 590 } 591 } 592 } 593 } 594 595 if ($keep_comment == '') { 596 $keep_comment = <<<HELP 597 ## For adding information to the start of this language file, 598 ## you can use comments starting with "##". Those comments will 599 ## be kept intact when a new language file is generated by the 600 ## language file maintenance software. 601 HELP; 602 } 603 604 // These aren't inside $PHORUM, but we put them there so we have 605 // access to them later on. 606 $PHORUM['STORE']['language_hide'] = $language_hide; 607 $PHORUM['STORE']['language'] = $language; 608 $PHORUM['STORE']['keep_comment'] = $keep_comment; 609 $PHORUM['STORE']['DEPRECATED'] = $DEPRECATED; 610 611 if (TOKEN_DEBUGGER){ 612 print_var($PHORUM); 613 } 614 return $PHORUM; 615 } 616 617 618 // ====================================================================== 619 // Extracting language strings from distribution files 620 // ====================================================================== 621 622 function phorum_extract_language_strings() 623 { 624 global $extract_strings; 625 $extract_strings = array(); 626 phorum_extract_language_strings_recurse("."); 627 return $extract_strings; 628 } 629 630 // This function processes directories recursively to search 631 // for language strings. 632 function phorum_extract_language_strings_recurse($path) 633 { 634 global $extract_strings; 635 636 $dh = opendir($path); 637 while (($f = readdir($dh))) 638 { 639 $file = "$path/$f"; 640 641 $ext = null; 642 if (preg_match('/\.(\w+)$/', $f, $m)) $ext = $m[1]; 643 644 // Skip what we do not want to index. 645 if ($f == "." || $f == "..") continue; // this and parent dir 646 if ($f == ".svn") continue; // SVN data directories 647 if ($f == "lang") continue; // language files 648 if ($f == "mods") continue; // mods 649 if ($f == "docs") continue; // documentation 650 if ($f == "cache") continue; // the cache directory 651 652 653 if (preg_match('/\.(php|tpl)$/', $file)) { 654 $fp = fopen($file, "r"); 655 if (! $fp) die("Can't read file '$file'"); 656 while (($line = fgets($fp, 1024))) { 657 $strings = array(); 658 if (preg_match_all('/LANG->([\w_-]+)/', $line, $m, PREG_SET_ORDER)) { 659 $strings = array_merge($strings, $m); 660 } 661 if (preg_match_all('/\$PHORUM\[["\']DATA["\']\]\[["\']LANG["\']\]\[["\']([^"\']+)["\']\]/', $line, $m, PREG_SET_ORDER)) { 662 $strings = array_merge($strings, $m); 663 } 664 foreach ($strings as $string) { 665 if (! isset($extract_strings[$string[1]])) { 666 $extract_strings[$string[1]] = array('files'=>array()); 667 } 668 $extract_strings[$string[1]]['files'][$file]++; 669 $extract_strings[$string[1]]['source'][$string[0]]++; 670 671 } 672 } 673 fclose($fp); 674 } 675 676 if (is_dir($file)) { 677 phorum_extract_language_strings_recurse($file); 678 } 679 } 680 closedir($dh); 681 } 682 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Thu Nov 29 12:22:27 2007 | par Balluche grâce à PHPXref 0.7 |
|