[ Index ] |
|
Code source de Symfony 1.0.0 |
1 <?php 2 3 /* 4 * This file is part of the symfony package. 5 * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11 /** 12 * Admin generator. 13 * 14 * This class generates an admin module. 15 * 16 * This class calls two ORM specific methods: 17 * getAllColumns() 18 * and 19 * getAdminColumnForField($field, $flag = null) 20 * 21 * @package symfony 22 * @subpackage generator 23 * @author Fabien Potencier <fabien.potencier@symfony-project.com> 24 * @version SVN: $Id: sfPropelAdminGenerator.class.php 2625 2006-11-07 10:36:14Z fabien $ 25 */ 26 abstract class sfAdminGenerator extends sfCrudGenerator 27 { 28 protected 29 $fields = array(); 30 31 /** 32 * Returns HTML code for a help icon. 33 * 34 * @param string The column name 35 * @param string The field type (list, edit) 36 * 37 * @return string HTML code 38 */ 39 public function getHelpAsIcon($column, $type = '') 40 { 41 $help = $this->getParameterValue($type.'.fields.'.$column->getName().'.help'); 42 if ($help) 43 { 44 return "[?php echo image_tag(sfConfig::get('sf_admin_web_dir').'/images/help.png', array('align' => 'absmiddle', 'alt' => __('".$this->escapeString($help)."'), 'title' => __('".$this->escapeString($help)."'))) ?]"; 45 } 46 47 return ''; 48 } 49 50 /** 51 * Returns HTML code for a help text. 52 * 53 * @param string The column name 54 * @param string The field type (list, edit) 55 * 56 * @return string HTML code 57 */ 58 public function getHelp($column, $type = '') 59 { 60 $help = $this->getParameterValue($type.'.fields.'.$column->getName().'.help'); 61 if ($help) 62 { 63 return "<div class=\"sf_admin_edit_help\">[?php echo __('".$this->escapeString($help)."') ?]</div>"; 64 } 65 66 return ''; 67 } 68 69 /** 70 * Returns HTML code for an action button. 71 * 72 * @param string The action name 73 * @param array The parameters 74 * @param boolean Whether to add a primary key link or not 75 * 76 * @return string HTML code 77 */ 78 public function getButtonToAction($actionName, $params, $pk_link = false) 79 { 80 $params = (array) $params; 81 $options = isset($params['params']) ? sfToolkit::stringToArray($params['params']) : array(); 82 $method = 'button_to'; 83 $li_class = ''; 84 $only_for = isset($params['only_for']) ? $params['only_for'] : null; 85 86 // default values 87 if ($actionName[0] == '_') 88 { 89 $actionName = substr($actionName, 1); 90 $default_name = strtr($actionName, '_', ' '); 91 $default_icon = sfConfig::get('sf_admin_web_dir').'/images/'.$actionName.'_icon.png'; 92 $default_action = $actionName; 93 $default_class = 'sf_admin_action_'.$actionName; 94 95 if ($actionName == 'save' || $actionName == 'save_and_add' || $actionName == 'save_and_list') 96 { 97 $method = 'submit_tag'; 98 $options['name'] = $actionName; 99 } 100 101 if ($actionName == 'delete') 102 { 103 $options['post'] = true; 104 if (!isset($options['confirm'])) 105 { 106 $options['confirm'] = 'Are you sure?'; 107 } 108 109 $li_class = 'float-left'; 110 111 $only_for = 'edit'; 112 } 113 } 114 else 115 { 116 $default_name = strtr($actionName, '_', ' '); 117 $default_icon = sfConfig::get('sf_admin_web_dir').'/images/default_icon.png'; 118 $default_action = 'List'.sfInflector::camelize($actionName); 119 $default_class = ''; 120 } 121 122 $name = isset($params['name']) ? $params['name'] : $default_name; 123 $icon = isset($params['icon']) ? sfToolkit::replaceConstants($params['icon']) : $default_icon; 124 $action = isset($params['action']) ? $params['action'] : $default_action; 125 $url_params = $pk_link ? '?'.$this->getPrimaryKeyUrlParams() : '\''; 126 127 if (!isset($options['class'])) 128 { 129 if ($default_class) 130 { 131 $options['class'] = $default_class; 132 } 133 else 134 { 135 $options['style'] = 'background: #ffc url('.$icon.') no-repeat 3px 2px'; 136 } 137 } 138 139 $li_class = $li_class ? ' class="'.$li_class.'"' : ''; 140 141 $html = '<li'.$li_class.'>'; 142 143 if ($only_for == 'edit') 144 { 145 $html .= '[?php if ('.$this->getPrimaryKeyIsSet().'): ?]'."\n"; 146 } 147 else if ($only_for == 'create') 148 { 149 $html .= '[?php if (!'.$this->getPrimaryKeyIsSet().'): ?]'."\n"; 150 } 151 else if ($only_for !== null) 152 { 153 throw new sfConfigurationException(sprintf('The "only_for" parameter can only takes "create" or "edit" as argument ("%s")', $only_for)); 154 } 155 156 if ($method == 'submit_tag') 157 { 158 $html .= '[?php echo submit_tag(__(\''.$name.'\'), '.var_export($options, true).') ?]'; 159 } 160 else 161 { 162 $phpOptions = var_export($options, true); 163 164 // little hack 165 $phpOptions = preg_replace("/'confirm' => '(.+?)(?<!\\\)'/", '\'confirm\' => __(\'$1\')', $phpOptions); 166 167 $html .= '[?php echo button_to(__(\''.$name.'\'), \''.$this->getModuleName().'/'.$action.$url_params.', '.$phpOptions.') ?]'; 168 } 169 170 if ($only_for !== null) 171 { 172 $html .= '[?php endif; ?]'."\n"; 173 } 174 175 $html .= '</li>'."\n"; 176 177 return $html; 178 } 179 180 /** 181 * Returns HTML code for an action link. 182 * 183 * @param string The action name 184 * @param array The parameters 185 * @param boolean Whether to add a primary key link or not 186 * 187 * @return string HTML code 188 */ 189 public function getLinkToAction($actionName, $params, $pk_link = false) 190 { 191 $options = isset($params['params']) ? sfToolkit::stringToArray($params['params']) : array(); 192 193 // default values 194 if ($actionName[0] == '_') 195 { 196 $actionName = substr($actionName, 1); 197 $name = $actionName; 198 $icon = sfConfig::get('sf_admin_web_dir').'/images/'.$actionName.'_icon.png'; 199 $action = $actionName; 200 201 if ($actionName == 'delete') 202 { 203 $options['post'] = true; 204 if (!isset($options['confirm'])) 205 { 206 $options['confirm'] = 'Are you sure?'; 207 } 208 } 209 } 210 else 211 { 212 $name = isset($params['name']) ? $params['name'] : $actionName; 213 $icon = isset($params['icon']) ? sfToolkit::replaceConstants($params['icon']) : sfConfig::get('sf_admin_web_dir').'/images/default_icon.png'; 214 $action = isset($params['action']) ? $params['action'] : 'List'.sfInflector::camelize($actionName); 215 } 216 217 $url_params = $pk_link ? '?'.$this->getPrimaryKeyUrlParams() : '\''; 218 219 $phpOptions = var_export($options, true); 220 221 // little hack 222 $phpOptions = preg_replace("/'confirm' => '(.+?)(?<!\\\)'/", '\'confirm\' => __(\'$1\')', $phpOptions); 223 224 return '<li>[?php echo link_to(image_tag(\''.$icon.'\', array(\'alt\' => __(\''.$name.'\'), \'title\' => __(\''.$name.'\'))), \''.$this->getModuleName().'/'.$action.$url_params.($options ? ', '.$phpOptions : '').') ?]</li>'."\n"; 225 } 226 227 /** 228 * Returns HTML code for a column in edit mode. 229 * 230 * @param string The column name 231 * @param array The parameters 232 * 233 * @return string HTML code 234 */ 235 public function getColumnEditTag($column, $params = array()) 236 { 237 // user defined parameters 238 $user_params = $this->getParameterValue('edit.fields.'.$column->getName().'.params'); 239 $user_params = is_array($user_params) ? $user_params : sfToolkit::stringToArray($user_params); 240 $params = $user_params ? array_merge($params, $user_params) : $params; 241 242 if ($column->isComponent()) 243 { 244 return "get_component('".$this->getModuleName()."', '".$column->getName()."', array('type' => 'edit', '{$this->getSingularName()}' => \${$this->getSingularName()}))"; 245 } 246 else if ($column->isPartial()) 247 { 248 return "get_partial('".$column->getName()."', array('type' => 'edit', '{$this->getSingularName()}' => \${$this->getSingularName()}))"; 249 } 250 251 // default control name 252 $params = array_merge(array('control_name' => $this->getSingularName().'['.$column->getName().']'), $params); 253 254 // default parameter values 255 $type = $column->getCreoleType(); 256 if ($type == CreoleTypes::DATE) 257 { 258 $params = array_merge(array('rich' => true, 'calendar_button_img' => sfConfig::get('sf_admin_web_dir').'/images/date.png'), $params); 259 } 260 else if ($type == CreoleTypes::TIMESTAMP) 261 { 262 $params = array_merge(array('rich' => true, 'withtime' => true, 'calendar_button_img' => sfConfig::get('sf_admin_web_dir').'/images/date.png'), $params); 263 } 264 265 // user sets a specific tag to use 266 if ($inputType = $this->getParameterValue('edit.fields.'.$column->getName().'.type')) 267 { 268 if ($inputType == 'plain') 269 { 270 return $this->getColumnListTag($column, $params); 271 } 272 else 273 { 274 return $this->getPHPObjectHelper($inputType, $column, $params); 275 } 276 } 277 278 // guess the best tag to use with column type 279 return parent::getCrudColumnEditTag($column, $params); 280 } 281 282 /** 283 * Returns all column categories. 284 * 285 * @param string The parameter name 286 * 287 * @return array The column categories 288 */ 289 public function getColumnCategories($paramName) 290 { 291 if (is_array($this->getParameterValue($paramName))) 292 { 293 $fields = $this->getParameterValue($paramName); 294 295 // do we have categories? 296 if (!isset($fields[0])) 297 { 298 return array_keys($fields); 299 } 300 301 } 302 303 return array('NONE'); 304 } 305 306 /** 307 * Wraps content with a credential condition. 308 * 309 * @param string The content 310 * @param array The parameters 311 * 312 * @return string HTML code 313 */ 314 public function addCredentialCondition($content, $params = array()) 315 { 316 if (isset($params['credentials'])) 317 { 318 $credentials = str_replace("\n", ' ', var_export($params['credentials'], true)); 319 320 return <<<EOF 321 [?php if (\$sf_user->hasCredential($credentials)): ?] 322 $content 323 [?php endif; ?] 324 EOF; 325 } 326 else 327 { 328 return $content; 329 } 330 } 331 332 /** 333 * Gets sfAdminColumn objects for a given category. 334 * 335 * @param string The parameter name 336 * 337 * @return array sfAdminColumn array 338 */ 339 public function getColumns($paramName, $category = 'NONE') 340 { 341 $phpNames = array(); 342 343 // user has set a personnalized list of fields? 344 $fields = $this->getParameterValue($paramName); 345 if (is_array($fields)) 346 { 347 // categories? 348 if (isset($fields[0])) 349 { 350 // simulate a default one 351 $fields = array('NONE' => $fields); 352 } 353 354 if (!$fields) 355 { 356 return array(); 357 } 358 359 foreach ($fields[$category] as $field) 360 { 361 list($field, $flags) = $this->splitFlag($field); 362 363 $phpNames[] = $this->getAdminColumnForField($field, $flags); 364 } 365 } 366 else 367 { 368 // no, just return the full list of columns in table 369 return $this->getAllColumns(); 370 } 371 372 return $phpNames; 373 } 374 375 /** 376 * Gets modifier flags from a column name. 377 * 378 * @param string The column name 379 * 380 * @return array An array of detected flags 381 */ 382 public function splitFlag($text) 383 { 384 $flags = array(); 385 while (in_array($text[0], array('=', '-', '+', '_', '~'))) 386 { 387 $flags[] = $text[0]; 388 $text = substr($text, 1); 389 } 390 391 return array($text, $flags); 392 } 393 394 /** 395 * Gets a parameter value. 396 * 397 * @param string The key name 398 * @param mixed The default value 399 * 400 * @return mixed The parameter value 401 */ 402 public function getParameterValue($key, $default = null) 403 { 404 if (preg_match('/^([^\.]+)\.fields\.(.+)$/', $key, $matches)) 405 { 406 return $this->getFieldParameterValue($matches[2], $matches[1], $default); 407 } 408 else 409 { 410 return $this->getValueFromKey($key, $default); 411 } 412 } 413 414 /** 415 * Gets a field parameter value. 416 * 417 * @param string The key name 418 * @param string The type (list, edit) 419 * @param mixed The default value 420 * 421 * @return mixed The parameter value 422 */ 423 protected function getFieldParameterValue($key, $type = '', $default = null) 424 { 425 $retval = $this->getValueFromKey($type.'.fields.'.$key, $default); 426 if ($retval !== null) 427 { 428 return $retval; 429 } 430 431 $retval = $this->getValueFromKey('fields.'.$key, $default); 432 if ($retval !== null) 433 { 434 return $retval; 435 } 436 437 if (preg_match('/\.name$/', $key)) 438 { 439 // default field.name 440 return sfInflector::humanize(($pos = strpos($key, '.')) ? substr($key, 0, $pos) : $key); 441 } 442 else 443 { 444 return null; 445 } 446 } 447 448 /** 449 * Gets the value for a given key. 450 * 451 * @param string The key name 452 * @param mixed The default value 453 * 454 * @return mixed The key value 455 */ 456 protected function getValueFromKey($key, $default = null) 457 { 458 $ref =& $this->params; 459 $parts = explode('.', $key); 460 $count = count($parts); 461 for ($i = 0; $i < $count; $i++) 462 { 463 $partKey = $parts[$i]; 464 if (!isset($ref[$partKey])) 465 { 466 return $default; 467 } 468 469 if ($count == $i + 1) 470 { 471 return $ref[$partKey]; 472 } 473 else 474 { 475 $ref =& $ref[$partKey]; 476 } 477 } 478 479 return $default; 480 } 481 482 /** 483 * Wraps a content for I18N. 484 * 485 * @param string The key name 486 * @param string The defaul value 487 * 488 * @return string HTML code 489 */ 490 public function getI18NString($key, $default = null, $withEcho = true) 491 { 492 $value = $this->escapeString($this->getParameterValue($key, $default)); 493 494 // find %%xx%% strings 495 preg_match_all('/%%([^%]+)%%/', $value, $matches, PREG_PATTERN_ORDER); 496 $this->params['tmp']['display'] = array(); 497 foreach ($matches[1] as $name) 498 { 499 $this->params['tmp']['display'][] = $name; 500 } 501 502 $vars = array(); 503 foreach ($this->getColumns('tmp.display') as $column) 504 { 505 if ($column->isLink()) 506 { 507 $vars[] = '\'%%'.$column->getName().'%%\' => link_to('.$this->getColumnListTag($column).', \''.$this->getModuleName().'/edit?'.$this->getPrimaryKeyUrlParams().')'; 508 } 509 elseif ($column->isPartial()) 510 { 511 $vars[] = '\'%%_'.$column->getName().'%%\' => '.$this->getColumnListTag($column); 512 } 513 else if ($column->isComponent()) 514 { 515 $vars[] = '\'%%_'.$column->getName().'%%\' => '.$this->getColumnListTag($column); 516 } 517 else 518 { 519 $vars[] = '\'%%'.$column->getName().'%%\' => '.$this->getColumnListTag($column); 520 } 521 } 522 523 // strip all = signs 524 $value = preg_replace('/%%=([^%]+)%%/', '%%$1%%', $value); 525 526 $i18n = '__(\''.$value.'\', '."\n".'array('.implode(",\n", $vars).'))'; 527 528 return $withEcho ? '[?php echo '.$i18n.' ?]' : $i18n; 529 } 530 531 /** 532 * Replaces constants in a string. 533 * 534 * @param string 535 * 536 * @return string 537 */ 538 public function replaceConstants($value) 539 { 540 // find %%xx%% strings 541 preg_match_all('/%%([^%]+)%%/', $value, $matches, PREG_PATTERN_ORDER); 542 $this->params['tmp']['display'] = array(); 543 foreach ($matches[1] as $name) 544 { 545 $this->params['tmp']['display'][] = $name; 546 } 547 548 foreach ($this->getColumns('tmp.display') as $column) 549 { 550 $value = str_replace('%%'.$column->getName().'%%', '{'.$this->getColumnGetter($column, true, 'this->').'}', $value); 551 } 552 553 return $value; 554 } 555 556 /** 557 * Returns HTML code for a column in list mode. 558 * 559 * @param string The column name 560 * @param array The parameters 561 * 562 * @return string HTML code 563 */ 564 public function getColumnListTag($column, $params = array()) 565 { 566 $user_params = $this->getParameterValue('list.fields.'.$column->getName().'.params'); 567 $user_params = is_array($user_params) ? $user_params : sfToolkit::stringToArray($user_params); 568 $params = $user_params ? array_merge($params, $user_params) : $params; 569 570 $type = $column->getCreoleType(); 571 572 $columnGetter = $this->getColumnGetter($column, true); 573 574 if ($column->isComponent()) 575 { 576 return "get_component('".$this->getModuleName()."', '".$column->getName()."', array('type' => 'list', '{$this->getSingularName()}' => \${$this->getSingularName()}))"; 577 } 578 else if ($column->isPartial()) 579 { 580 return "get_partial('".$column->getName()."', array('type' => 'list', '{$this->getSingularName()}' => \${$this->getSingularName()}))"; 581 } 582 else if ($type == CreoleTypes::DATE || $type == CreoleTypes::TIMESTAMP) 583 { 584 $format = isset($params['date_format']) ? $params['date_format'] : ($type == CreoleTypes::DATE ? 'D' : 'f'); 585 return "($columnGetter !== null && $columnGetter !== '') ? format_date($columnGetter, \"$format\") : ''"; 586 } 587 elseif ($type == CreoleTypes::BOOLEAN) 588 { 589 return "$columnGetter ? image_tag(sfConfig::get('sf_admin_web_dir').'/images/tick.png') : ' '"; 590 } 591 else 592 { 593 return "$columnGetter"; 594 } 595 } 596 597 /** 598 * Returns HTML code for a column in filter mode. 599 * 600 * @param string The column name 601 * @param array The parameters 602 * 603 * @return string HTML code 604 */ 605 public function getColumnFilterTag($column, $params = array()) 606 { 607 $user_params = $this->getParameterValue('list.fields.'.$column->getName().'.params'); 608 $user_params = is_array($user_params) ? $user_params : sfToolkit::stringToArray($user_params); 609 $params = $user_params ? array_merge($params, $user_params) : $params; 610 611 if ($column->isComponent()) 612 { 613 return "get_component('".$this->getModuleName()."', '".$column->getName()."', array('type' => 'list'))"; 614 } 615 else if ($column->isPartial()) 616 { 617 return "get_partial('".$column->getName()."', array('type' => 'filter', 'filters' => \$filters))"; 618 } 619 620 $type = $column->getCreoleType(); 621 622 $default_value = "isset(\$filters['".$column->getName()."']) ? \$filters['".$column->getName()."'] : null"; 623 $unquotedName = 'filters['.$column->getName().']'; 624 $name = "'$unquotedName'"; 625 626 if ($column->isForeignKey()) 627 { 628 $params = $this->getObjectTagParams($params, array('include_blank' => true, 'related_class'=>$this->getRelatedClassName($column), 'text_method'=>'__toString', 'control_name'=>$unquotedName)); 629 return "object_select_tag($default_value, null, $params)"; 630 631 } 632 else if ($type == CreoleTypes::DATE) 633 { 634 // rich=false not yet implemented 635 $params = $this->getObjectTagParams($params, array('rich' => true, 'calendar_button_img' => sfConfig::get('sf_admin_web_dir').'/images/date.png')); 636 return "input_date_range_tag($name, $default_value, $params)"; 637 } 638 else if ($type == CreoleTypes::TIMESTAMP) 639 { 640 // rich=false not yet implemented 641 $params = $this->getObjectTagParams($params, array('rich' => true, 'withtime' => true, 'calendar_button_img' => sfConfig::get('sf_admin_web_dir').'/images/date.png')); 642 return "input_date_range_tag($name, $default_value, $params)"; 643 } 644 else if ($type == CreoleTypes::BOOLEAN) 645 { 646 $defaultIncludeCustom = '__("yes or no")'; 647 648 $option_params = $this->getObjectTagParams($params, array('include_custom' => $defaultIncludeCustom)); 649 $params = $this->getObjectTagParams($params); 650 651 // little hack 652 $option_params = preg_replace("/'".preg_quote($defaultIncludeCustom)."'/", $defaultIncludeCustom, $option_params); 653 654 $options = "options_for_select(array(1 => __('yes'), 0 => __('no')), $default_value, $option_params)"; 655 656 return "select_tag($name, $options, $params)"; 657 } 658 else if ($type == CreoleTypes::CHAR || $type == CreoleTypes::VARCHAR || $type == CreoleTypes::TEXT || $type == CreoleTypes::LONGVARCHAR) 659 { 660 $size = ($column->getSize() < 15 ? $column->getSize() : 15); 661 $params = $this->getObjectTagParams($params, array('size' => $size)); 662 return "input_tag($name, $default_value, $params)"; 663 } 664 else if ($type == CreoleTypes::INTEGER || $type == CreoleTypes::TINYINT || $type == CreoleTypes::SMALLINT || $type == CreoleTypes::BIGINT) 665 { 666 $params = $this->getObjectTagParams($params, array('size' => 7)); 667 return "input_tag($name, $default_value, $params)"; 668 } 669 else if ($type == CreoleTypes::FLOAT || $type == CreoleTypes::DOUBLE || $type == CreoleTypes::DECIMAL || $type == CreoleTypes::NUMERIC || $type == CreoleTypes::REAL) 670 { 671 $params = $this->getObjectTagParams($params, array('size' => 7)); 672 return "input_tag($name, $default_value, $params)"; 673 } 674 else 675 { 676 $params = $this->getObjectTagParams($params, array('disabled' => true)); 677 return "input_tag($name, $default_value, $params)"; 678 } 679 } 680 681 /** 682 * Escapes a string. 683 * 684 * @param string 685 * 686 * @param string 687 */ 688 protected function escapeString($string) 689 { 690 return preg_replace('/\'/', '\\\'', $string); 691 } 692 } 693 694 /** 695 * Propel admin generator column. 696 * 697 * @package symfony 698 * @subpackage generator 699 * @author Fabien Potencier <fabien.potencier@symfony-project.com> 700 * @version SVN: $Id: sfPropelAdminGenerator.class.php 2625 2006-11-07 10:36:14Z fabien $ 701 */ 702 class sfAdminColumn 703 { 704 protected 705 $phpName = '', 706 $column = null, 707 $flags = array(); 708 709 /** 710 * Constructor. 711 * 712 * @param string The column php name 713 * @param string The column name 714 * @param array The column flags 715 */ 716 public function __construct($phpName, $column = null, $flags = array()) 717 { 718 $this->phpName = $phpName; 719 $this->column = $column; 720 $this->flags = (array) $flags; 721 } 722 723 /** 724 * Returns true if the column maps a database column. 725 * 726 * @return boolean true if the column maps a database column, false otherwise 727 */ 728 public function isReal() 729 { 730 return $this->column ? true : false; 731 } 732 733 /** 734 * Gets the name of the column. 735 * 736 * @return string The column name 737 */ 738 public function getName() 739 { 740 return sfInflector::underscore($this->phpName); 741 } 742 743 /** 744 * Returns true if the column is a partial. 745 * 746 * @return boolean true if the column is a partial, false otherwise 747 */ 748 public function isPartial() 749 { 750 return in_array('_', $this->flags) ? true : false; 751 } 752 753 /** 754 * Returns true if the column is a component. 755 * 756 * @return boolean true if the column is a component, false otherwise 757 */ 758 public function isComponent() 759 { 760 return in_array('~', $this->flags) ? true : false; 761 } 762 763 /** 764 * Returns true if the column has a link. 765 * 766 * @return boolean true if the column has a link, false otherwise 767 */ 768 public function isLink() 769 { 770 return (in_array('=', $this->flags) || $this->isPrimaryKey()) ? true : false; 771 } 772 773 /** 774 * Gets the php name of the column. 775 * 776 * @return string The php name 777 */ 778 public function getPhpName() 779 { 780 return $this->phpName; 781 } 782 783 // FIXME: those methods are only used in the propel admin generator 784 public function __call($name, $arguments) 785 { 786 return $this->column ? $this->column->$name() : null; 787 } 788 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Fri Mar 16 22:42:14 2007 | par Balluche grâce à PHPXref 0.7 |