| [ Index ] |
|
Code source de PRADO 3.0.6 |
1 <?php 2 /** 3 * TPageService class file. 4 * 5 * @author Qiang Xue <qiang.xue@gmail.com> 6 * @link http://www.pradosoft.com/ 7 * @copyright Copyright © 2005 PradoSoft 8 * @license http://www.pradosoft.com/license/ 9 * @version $Id: TPageService.php 1508 2006-11-25 20:42:54Z xue $ 10 * @package System.Web.Services 11 */ 12 13 /** 14 * Include classes to be used by page service 15 */ 16 Prado::using('System.Web.UI.TPage'); 17 Prado::using('System.Web.UI.TTemplateManager'); 18 Prado::using('System.Web.UI.TThemeManager'); 19 20 /** 21 * TPageService class. 22 * 23 * TPageService implements the service for serving user page requests. 24 * 25 * Pages that are available to client users are stored under a directory specified by 26 * {@link setBasePath BasePath}. The directory may contain subdirectories. 27 * Pages serving for a similar goal are usually placed under the same directory. 28 * A directory may contain a configuration file <b>config.xml</b> whose content 29 * is similar to that of application configuration file. 30 * 31 * A page is requested via page path, which is a dot-connected directory names 32 * appended by the page name. Assume '<BasePath>/Users/Admin' is the directory 33 * containing the page 'Update'. Then the page can be requested via 'Users.Admin.Update'. 34 * By default, the {@link setBasePath BasePath} of the page service is the "pages" 35 * directory under the application base path. You may change this default 36 * by setting {@link setBasePath BasePath} with a different path you prefer. 37 * 38 * Page name refers to the file name (without extension) of the page template. 39 * In order to differentiate from the common control template files, the extension 40 * name of the page template files must be '.page'. If there is a PHP file with 41 * the same page name under the same directory as the template file, that file 42 * will be considered as the page class file and the file name is the page class name. 43 * If such a file is not found, the page class is assumed as {@link TPage}. 44 * 45 * Modules can be configured and loaded in page directory configurations. 46 * Configuration of a module in a subdirectory will overwrite its parent 47 * directory's configuration, if both configurations refer to the same module. 48 * 49 * By default, TPageService will automatically load two modules: 50 * - {@link TTemplateManager} : manages page and control templates 51 * - {@link TThemeManager} : manages themes used in a Prado application 52 * 53 * In page directory configurations, static authorization rules can also be specified, 54 * which governs who and which roles can access particular pages. 55 * Refer to {@link TAuthorizationRule} for more details about authorization rules. 56 * Page authorization rules can be configured within an <authorization> tag in 57 * each page directory configuration as follows, 58 * <authorization> 59 * <deny pages="Update" users="?" /> 60 * <allow pages="Admin" roles="administrator" /> 61 * <deny pages="Admin" users="*" /> 62 * </authorization> 63 * where the 'pages' attribute may be filled with a sequence of comma-separated 64 * page IDs. If 'pages' attribute does not appear in a rule, the rule will be 65 * applied to all pages in this directory and all subdirectories (recursively). 66 * Application of authorization rules are in a bottom-up fashion, starting from 67 * the directory containing the requested page up to all parent directories. 68 * The first matching rule will be used. The last rule always allows all users 69 * accessing to any resources. 70 * 71 * @author Qiang Xue <qiang.xue@gmail.com> 72 * @version $Id: TPageService.php 1508 2006-11-25 20:42:54Z xue $ 73 * @package System.Web.Services 74 * @since 3.0 75 */ 76 class TPageService extends TService 77 { 78 /** 79 * Configuration file name 80 */ 81 const CONFIG_FILE='config.xml'; 82 /** 83 * Default base path 84 */ 85 const DEFAULT_BASEPATH='pages'; 86 /** 87 * Prefix of ID used for storing parsed configuration in cache 88 */ 89 const CONFIG_CACHE_PREFIX='prado:pageservice:'; 90 /** 91 * Page template file extension 92 */ 93 const PAGE_FILE_EXT='.page'; 94 /** 95 * @var string root path of pages 96 */ 97 private $_basePath=null; 98 /** 99 * @var string base path class in namespace format 100 */ 101 private $_basePageClass='TPage'; 102 /** 103 * @var string default page 104 */ 105 private $_defaultPage='Home'; 106 /** 107 * @var string requested page (path) 108 */ 109 private $_pagePath=null; 110 /** 111 * @var TPage the requested page 112 */ 113 private $_page=null; 114 /** 115 * @var array list of initial page property values 116 */ 117 private $_properties; 118 /** 119 * @var boolean whether service is initialized 120 */ 121 private $_initialized=false; 122 /** 123 * @var TThemeManager theme manager 124 */ 125 private $_themeManager=null; 126 /** 127 * @var TTemplateManager template manager 128 */ 129 private $_templateManager=null; 130 131 /** 132 * Constructor. 133 * Sets default service ID to 'page'. 134 */ 135 public function __construct() 136 { 137 $this->setID('page'); 138 } 139 140 /** 141 * Initializes the service. 142 * This method is required by IService interface and is invoked by application. 143 * @param TXmlElement service configuration 144 */ 145 public function init($config) 146 { 147 Prado::trace("Initializing TPageService",'System.Web.Services.TPageService'); 148 149 $this->getApplication()->setPageService($this); 150 151 $pageConfig=$this->loadPageConfig($this->getRequestedPagePath(),$config); 152 153 $this->initPageContext($pageConfig); 154 155 $this->_initialized=true; 156 } 157 158 /** 159 * Initializes page context. 160 * Page context includes path alias settings, namespace usages, 161 * parameter initialization, module loadings, page initial properties 162 * and authorization rules. 163 * @param TPageConfiguration 164 */ 165 protected function initPageContext($pageConfig) 166 { 167 $application=$this->getApplication(); 168 169 // set path aliases and using namespaces 170 foreach($pageConfig->getAliases() as $alias=>$path) 171 Prado::setPathOfAlias($alias,$path); 172 foreach($pageConfig->getUsings() as $using) 173 Prado::using($using); 174 175 // initial page properties (to be set when page runs) 176 $this->_properties=$pageConfig->getProperties(); 177 178 // load parameters 179 $parameters=$application->getParameters(); 180 foreach($pageConfig->getParameters() as $id=>$parameter) 181 { 182 if(is_array($parameter)) 183 { 184 $component=Prado::createComponent($parameter[0]); 185 foreach($parameter[1] as $name=>$value) 186 $component->setSubProperty($name,$value); 187 $parameters->add($id,$component); 188 } 189 else 190 $parameters->add($id,$parameter); 191 } 192 193 // load modules specified in page directory config 194 $modules=array(); 195 foreach($pageConfig->getModules() as $id=>$moduleConfig) 196 { 197 Prado::trace("Loading module $id ({$moduleConfig[0]})",'System.Web.Services.TPageService'); 198 $module=Prado::createComponent($moduleConfig[0]); 199 if(is_string($id)) 200 $application->setModule($id,$module); 201 foreach($moduleConfig[1] as $name=>$value) 202 $module->setSubProperty($name,$value); 203 $modules[]=array($module,$moduleConfig[2]); 204 } 205 foreach($modules as $module) 206 $module[0]->init($module[1]); 207 208 $application->getAuthorizationRules()->mergeWith($pageConfig->getRules()); 209 } 210 211 /** 212 * Determines the requested page path. 213 * @return string page path requested 214 */ 215 protected function determineRequestedPagePath() 216 { 217 $pagePath=$this->getRequest()->getServiceParameter(); 218 if(empty($pagePath)) 219 $pagePath=$this->getDefaultPage(); 220 return $pagePath; 221 } 222 223 /** 224 * Collects configuration for a page. 225 * @param string page path in the format of Path.To.Page 226 * @param TXmlElement additional configuration 227 * @return TPageConfiguration 228 */ 229 protected function loadPageConfig($pagePath,$config=null) 230 { 231 $application=$this->getApplication(); 232 if(($cache=$application->getCache())===null) 233 { 234 $pageConfig=new TPageConfiguration; 235 if($config!==null) 236 $pageConfig->loadXmlElement($config,$application->getBasePath(),null); 237 $pageConfig->loadConfigurationFiles($pagePath,$this->getBasePath()); 238 } 239 else 240 { 241 $configCached=true; 242 $currentTimestamp=array(); 243 $arr=$cache->get(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath); 244 if(is_array($arr)) 245 { 246 list($pageConfig,$timestamps)=$arr; 247 if($application->getMode()!==TApplicationMode::Performance) 248 { 249 foreach($timestamps as $fileName=>$timestamp) 250 { 251 if($fileName===0) // application config file 252 { 253 $appConfigFile=$application->getConfigurationFile(); 254 $currentTimestamp[0]=$appConfigFile===null?0:@filemtime($appConfigFile); 255 if($currentTimestamp[0]>$timestamp || ($timestamp>0 && !$currentTimestamp[0])) 256 $configCached=false; 257 } 258 else 259 { 260 $currentTimestamp[$fileName]=@filemtime($fileName); 261 if($currentTimestamp[$fileName]>$timestamp || ($timestamp>0 && !$currentTimestamp[$fileName])) 262 $configCached=false; 263 } 264 } 265 } 266 } 267 else 268 { 269 $configCached=false; 270 $paths=explode('.',$pagePath); 271 $configPath=$this->getBasePath(); 272 foreach($paths as $path) 273 { 274 $configFile=$configPath.'/'.self::CONFIG_FILE; 275 $currentTimestamp[$configFile]=@filemtime($configFile); 276 $configPath.='/'.$path; 277 } 278 $appConfigFile=$application->getConfigurationFile(); 279 $currentTimestamp[0]=$appConfigFile===null?0:@filemtime($appConfigFile); 280 } 281 if(!$configCached) 282 { 283 $pageConfig=new TPageConfiguration; 284 if($config!==null) 285 $pageConfig->loadXmlElement($config,$application->getBasePath(),null); 286 $pageConfig->loadConfigurationFiles($pagePath,$this->getBasePath()); 287 $cache->set(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath,array($pageConfig,$currentTimestamp)); 288 } 289 } 290 return $pageConfig; 291 } 292 293 /** 294 * @return TTemplateManager template manager 295 */ 296 public function getTemplateManager() 297 { 298 if(!$this->_templateManager) 299 { 300 $this->_templateManager=new TTemplateManager; 301 $this->_templateManager->init(null); 302 } 303 return $this->_templateManager; 304 } 305 306 /** 307 * @param TTemplateManager template manager 308 */ 309 public function setTemplateManager(TTemplateManager $value) 310 { 311 $this->_templateManager=$value; 312 } 313 314 /** 315 * @return TThemeManager theme manager 316 */ 317 public function getThemeManager() 318 { 319 if(!$this->_themeManager) 320 { 321 $this->_themeManager=new TThemeManager; 322 $this->_themeManager->init(null); 323 } 324 return $this->_themeManager; 325 } 326 327 /** 328 * @param TThemeManager theme manager 329 */ 330 public function setThemeManager(TThemeManager $value) 331 { 332 $this->_themeManager=$value; 333 } 334 335 /** 336 * @return string the requested page path 337 */ 338 public function getRequestedPagePath() 339 { 340 if($this->_pagePath===null) 341 { 342 $this->_pagePath=strtr($this->determineRequestedPagePath(),'/\\','..'); 343 if(empty($this->_pagePath)) 344 throw new THttpException(404,'pageservice_page_required'); 345 } 346 return $this->_pagePath; 347 } 348 349 /** 350 * @return TPage the requested page 351 */ 352 public function getRequestedPage() 353 { 354 return $this->_page; 355 } 356 357 /** 358 * @return string default page path to be served if no explicit page is request. Defaults to 'Home'. 359 */ 360 public function getDefaultPage() 361 { 362 return $this->_defaultPage; 363 } 364 365 /** 366 * @param string default page path to be served if no explicit page is request 367 * @throws TInvalidOperationException if the page service is initialized 368 */ 369 public function setDefaultPage($value) 370 { 371 if($this->_initialized) 372 throw new TInvalidOperationException('pageservice_defaultpage_unchangeable'); 373 else 374 $this->_defaultPage=$value; 375 } 376 377 /** 378 * @return string the root directory for storing pages. Defaults to the 'pages' directory under the application base path. 379 */ 380 public function getBasePath() 381 { 382 if($this->_basePath===null) 383 { 384 $basePath=$this->getApplication()->getBasePath().'/'.self::DEFAULT_BASEPATH; 385 if(($this->_basePath=realpath($basePath))===false || !is_dir($this->_basePath)) 386 throw new TConfigurationException('pageservice_basepath_invalid',$basePath); 387 } 388 return $this->_basePath; 389 } 390 391 /** 392 * @param string root directory (in namespace form) storing pages 393 * @throws TInvalidOperationException if the service is initialized already or basepath is invalid 394 */ 395 public function setBasePath($value) 396 { 397 if($this->_initialized) 398 throw new TInvalidOperationException('pageservice_basepath_unchangeable'); 399 else if(($path=Prado::getPathOfNamespace($value))===null || !is_dir($path)) 400 throw new TConfigurationException('pageservice_basepath_invalid',$value); 401 $this->_basePath=realpath($path); 402 } 403 404 /** 405 * Sets the base page class name (in namespace format). 406 * If a page only has a template file without page class file, 407 * this base page class will be instantiated. 408 * @param string class name 409 */ 410 public function setBasePageClass($value) 411 { 412 $this->_basePageClass=$value; 413 } 414 415 /** 416 * @return string base page class name in namespace format. Defaults to 'TPage'. 417 */ 418 public function getBasePageClass() 419 { 420 return $this->_basePageClass; 421 } 422 423 /** 424 * Runs the service. 425 * This will create the requested page, initializes it with the property values 426 * specified in the configuration, and executes the page. 427 */ 428 public function run() 429 { 430 Prado::trace("Running page service",'System.Web.Services.TPageService'); 431 $path=$this->getBasePath().'/'.strtr($this->getRequestedPagePath(),'.','/'); 432 if(is_file($path.self::PAGE_FILE_EXT)) 433 { 434 if(is_file($path.Prado::CLASS_FILE_EXT)) 435 { 436 $className=basename($path); 437 if(!class_exists($className,false)) 438 include_once($path.Prado::CLASS_FILE_EXT); 439 if(!class_exists($className,false)) 440 throw new TConfigurationException('pageservice_pageclass_unknown',$className); 441 } 442 else 443 $className=$this->getBasePageClass(); 444 445 $this->_page=Prado::createComponent($className); 446 447 $this->_page->setPagePath($this->getRequestedPagePath()); 448 // initialize page properties with those set in configurations 449 foreach($this->_properties as $name=>$value) 450 $this->_page->setSubProperty($name,$value); 451 452 // set page template 453 $this->_page->setTemplate($this->getTemplateManager()->getTemplateByFileName($path.self::PAGE_FILE_EXT)); 454 } 455 else 456 throw new THttpException(404,'pageservice_page_unknown',$this->getRequestedPagePath()); 457 458 $this->_page->run($this->getResponse()->createHtmlWriter()); 459 } 460 461 /** 462 * Constructs a URL with specified page path and GET parameters. 463 * @param string page path 464 * @param array list of GET parameters, null if no GET parameters required 465 * @param boolean whether to encode the ampersand in URL, defaults to true. 466 * @param boolean whether to encode the GET parameters (their names and values), defaults to true. 467 * @return string URL for the page and GET parameters 468 */ 469 public function constructUrl($pagePath,$getParams=null,$encodeAmpersand=true,$encodeGetItems=true) 470 { 471 return $this->getRequest()->constructUrl($this->getID(),$pagePath,$getParams,$encodeAmpersand,$encodeGetItems); 472 } 473 } 474 475 476 /** 477 * TPageConfiguration class 478 * 479 * TPageConfiguration represents the configuration for a page. 480 * The page is specified by a dot-connected path. 481 * Configurations along this path are merged together to be provided for the page. 482 * 483 * @author Qiang Xue <qiang.xue@gmail.com> 484 * @version $Id: TPageService.php 1508 2006-11-25 20:42:54Z xue $ 485 * @package System.Web.Services 486 * @since 3.0 487 */ 488 class TPageConfiguration extends TComponent 489 { 490 /** 491 * @var array list of page initial property values 492 */ 493 private $_properties=array(); 494 /** 495 * @var array list of namespaces to be used 496 */ 497 private $_usings=array(); 498 /** 499 * @var array list of path aliases 500 */ 501 private $_aliases=array(); 502 /** 503 * @var array list of module configurations 504 */ 505 private $_modules=array(); 506 /** 507 * @var array list of parameters 508 */ 509 private $_parameters=array(); 510 /** 511 * @var TAuthorizationRuleCollection list of authorization rules 512 */ 513 private $_rules=array(); 514 515 /** 516 * Returns list of page initial property values. 517 * Each array element represents a single property with the key 518 * being the property name and the value the initial property value. 519 * @return array list of page initial property values 520 */ 521 public function getProperties() 522 { 523 return $this->_properties; 524 } 525 526 /** 527 * Returns list of path alias definitions. 528 * The definitions are aggregated (top-down) from configuration files along the path 529 * to the specified page. Each array element represents a single alias definition, 530 * with the key being the alias name and the value the absolute path. 531 * @return array list of path alias definitions 532 */ 533 public function getAliases() 534 { 535 return $this->_aliases; 536 } 537 538 /** 539 * Returns list of namespaces to be used. 540 * The namespaces are aggregated (top-down) from configuration files along the path 541 * to the specified page. Each array element represents a single namespace usage, 542 * with the value being the namespace to be used. 543 * @return array list of namespaces to be used 544 */ 545 public function getUsings() 546 { 547 return $this->_usings; 548 } 549 550 /** 551 * Returns list of module configurations. 552 * The module configurations are aggregated (top-down) from configuration files 553 * along the path to the specified page. Each array element represents 554 * a single module configuration, with the key being the module ID and 555 * the value the module configuration. Each module configuration is 556 * stored in terms of an array with the following content 557 * ([0]=>module type, [1]=>module properties, [2]=>complete module configuration) 558 * The module properties are an array of property values indexed by property names. 559 * The complete module configuration is a TXmlElement object representing 560 * the raw module configuration which may contain contents enclosed within 561 * module tags. 562 * @return array list of module configurations to be used 563 */ 564 public function getModules() 565 { 566 return $this->_modules; 567 } 568 569 /** 570 * Returns list of parameter definitions. 571 * The parameter definitions are aggregated (top-down) from configuration files 572 * along the path to the specified page. Each array element represents 573 * a single parameter definition, with the key being the parameter ID and 574 * the value the parameter definition. A parameter definition can be either 575 * a string representing a string-typed parameter, or an array. 576 * The latter defines a component-typed parameter whose format is as follows, 577 * ([0]=>component type, [1]=>component properties) 578 * The component properties are an array of property values indexed by property names. 579 * @return array list of parameter definitions to be used 580 */ 581 public function getParameters() 582 { 583 return $this->_parameters; 584 } 585 586 /** 587 * Returns list of authorization rules. 588 * The authorization rules are aggregated (bottom-up) from configuration files 589 * along the path to the specified page. 590 * @return TAuthorizationRuleCollection collection of authorization rules 591 */ 592 public function getRules() 593 { 594 return $this->_rules; 595 } 596 597 /** 598 * Loads configuration for a page specified in a path format. 599 * @param string path to the page (dot-connected format) 600 * @param string root path for pages 601 */ 602 public function loadConfigurationFiles($pagePath,$basePath) 603 { 604 $paths=explode('.',$pagePath); 605 $page=array_pop($paths); 606 $path=$basePath; 607 foreach($paths as $p) 608 { 609 $this->loadFromFile($path.'/'.TPageService::CONFIG_FILE,null); 610 $path.='/'.$p; 611 } 612 $this->loadFromFile($path.'/'.TPageService::CONFIG_FILE,$page); 613 $this->_rules=new TAuthorizationRuleCollection($this->_rules); 614 } 615 616 /** 617 * Loads a specific config file. 618 * @param string config file name 619 * @param string page name, null if page is not required 620 */ 621 private function loadFromFile($fname,$page) 622 { 623 Prado::trace("Loading $page with file $fname",'System.Web.Services.TPageService'); 624 if(empty($fname) || !is_file($fname)) 625 return; 626 $dom=new TXmlDocument; 627 if($dom->loadFromFile($fname)) 628 $this->loadXmlElement($dom,dirname($fname),$page); 629 else 630 throw new TConfigurationException('pageserviceconf_file_invalid',$fname); 631 } 632 633 /** 634 * Loads a specific configuration xml element. 635 * @param TXmlElement config xml element 636 * @param string base path corresponding to this xml element 637 * @param string page name, null if page is not required 638 */ 639 public function loadXmlElement($dom,$configPath,$page) 640 { 641 // paths 642 if(($pathsNode=$dom->getElementByTagName('paths'))!==null) 643 { 644 foreach($pathsNode->getElementsByTagName('alias') as $aliasNode) 645 { 646 if(($id=$aliasNode->getAttribute('id'))!==null && ($p=$aliasNode->getAttribute('path'))!==null) 647 { 648 $p=str_replace('\\','/',$p); 649 $path=realpath(preg_match('/^\\/|.:\\//',$p)?$p:$configPath.'/'.$p); 650 if($path===false || !is_dir($path)) 651 throw new TConfigurationException('pageserviceconf_aliaspath_invalid',$id,$p,$configPath); 652 if(isset($this->_aliases[$id])) 653 throw new TConfigurationException('pageserviceconf_alias_redefined',$id,$configPath); 654 $this->_aliases[$id]=$path; 655 } 656 else 657 throw new TConfigurationException('pageserviceconf_alias_invalid',$configPath); 658 } 659 foreach($pathsNode->getElementsByTagName('using') as $usingNode) 660 { 661 if(($namespace=$usingNode->getAttribute('namespace'))!==null) 662 $this->_usings[]=$namespace; 663 else 664 throw new TConfigurationException('pageserviceconf_using_invalid',$configPath); 665 } 666 } 667 668 // modules 669 if(($modulesNode=$dom->getElementByTagName('modules'))!==null) 670 { 671 foreach($modulesNode->getElementsByTagName('module') as $node) 672 { 673 $properties=$node->getAttributes(); 674 $type=$properties->remove('class'); 675 $id=$properties->itemAt('id'); 676 if($type===null) 677 throw new TConfigurationException('pageserviceconf_moduletype_required',$id,$configPath); 678 $node->setParent(null); 679 if($id===null) 680 $this->_modules[]=array($type,$properties->toArray(),$node); 681 else 682 $this->_modules[$id]=array($type,$properties->toArray(),$node); 683 } 684 } 685 686 // parameters 687 if(($parametersNode=$dom->getElementByTagName('parameters'))!==null) 688 { 689 foreach($parametersNode->getElementsByTagName('parameter') as $node) 690 { 691 $properties=$node->getAttributes(); 692 if(($id=$properties->remove('id'))===null) 693 throw new TConfigurationException('pageserviceconf_parameter_invalid',$configPath); 694 if(($type=$properties->remove('class'))===null) 695 { 696 if(($value=$properties->remove('value'))===null) 697 $this->_parameters[$id]=$node; 698 else 699 $this->_parameters[$id]=$value; 700 } 701 else 702 $this->_parameters[$id]=array($type,$properties->toArray()); 703 } 704 } 705 706 // authorization 707 if(($authorizationNode=$dom->getElementByTagName('authorization'))!==null) 708 { 709 $rules=array(); 710 foreach($authorizationNode->getElements() as $node) 711 { 712 $pages=$node->getAttribute('pages'); 713 $ruleApplies=false; 714 if(empty($pages)) 715 $ruleApplies=true; 716 else if($page!==null) 717 { 718 $ps=explode(',',$pages); 719 foreach($ps as $p) 720 { 721 if(strcasecmp($page,trim($p))===0) 722 { 723 $ruleApplies=true; 724 break; 725 } 726 } 727 } 728 if($ruleApplies) 729 $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb')); 730 } 731 $this->_rules=array_merge($rules,$this->_rules); 732 } 733 734 // pages 735 if(($pagesNode=$dom->getElementByTagName('pages'))!==null) 736 { 737 $this->_properties=array_merge($this->_properties,$pagesNode->getAttributes()->toArray()); 738 if($page!==null) // at the page folder 739 { 740 foreach($pagesNode->getElementsByTagName('page') as $node) 741 { 742 $properties=$node->getAttributes(); 743 if(($id=$properties->itemAt('id'))===null) 744 throw new TConfigurationException('pageserviceconf_page_invalid',$configPath); 745 if(strcasecmp($id,$page)===0) 746 $this->_properties=array_merge($this->_properties,$properties->toArray()); 747 } 748 } 749 } 750 } 751 } 752 753 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Sun Feb 25 21:07:04 2007 | par Balluche grâce à PHPXref 0.7 |