[ Index ]
 

Code source de b2evolution 2.1.0-beta

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/blogs/inc/_core/model/dataobjects/ -> _dataobject.class.php (source)

   1  <?php
   2  /**

   3   * This file implements the abstract DataObject base class.

   4   *

   5   * This file is part of the evoCore framework - {@link http://evocore.net/}

   6   * See also {@link http://sourceforge.net/projects/evocms/}.

   7   *

   8   * @copyright (c)2003-2007 by Francois PLANQUE - {@link http://fplanque.net/}

   9   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.

  10   * Parts of this file are copyright (c)2005-2006 by PROGIDISTRI - {@link http://progidistri.com/}.

  11   *

  12   * {@internal License choice

  13   * - If you have received this file as part of a package, please find the license.txt file in

  14   *   the same folder or the closest folder above for complete license terms.

  15   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)

  16   *   then you must choose one of the following licenses before using the file:

  17   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php

  18   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php

  19   * }}

  20   *

  21   * {@internal Open Source relicensing agreement:

  22   * Daniel HAHLER grants Francois PLANQUE the right to license

  23   * Daniel HAHLER's contributions to this file and the b2evolution project

  24   * under any OSI approved OSS license (http://www.opensource.org/licenses/).

  25   *

  26   * PROGIDISTRI S.A.S. grants Francois PLANQUE the right to license

  27   * PROGIDISTRI S.A.S.'s contributions to this file and the b2evolution project

  28   * under any OSI approved OSS license (http://www.opensource.org/licenses/).

  29   * }}

  30   *

  31   * @package evocore

  32   *

  33   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}

  34   * @author fplanque: Francois PLANQUE

  35   * @author blueyed: Daniel HAHLER

  36   * @author mbruneau: Marc BRUNEAU / PROGIDISTRI

  37   *

  38   * @version $Id: _dataobject.class.php,v 1.1 2007/06/25 10:58:56 fplanque Exp $

  39   */
  40  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  41  
  42  /**

  43   * Data Object Base Class

  44   *

  45   * This is typically an abstract class, useful only when derived.

  46   *

  47   * @package evocore

  48   * @version beta

  49   * @abstract

  50   */
  51  class DataObject
  52  {
  53      /**

  54       * Unique ID of object in database

  55       *

  56       * Please use get/set functions to read or write this param

  57       *

  58       * @var int

  59       * @access protected

  60       */
  61      var $ID = 0;  // This will be the ID in the DB

  62  
  63      /**#@+

  64       * @access private

  65       */
  66      var $dbtablename;
  67      var $dbprefix;
  68      var $dbIDname;
  69      var $datecreated_field;
  70      var $datemodified_field;
  71      var $creator_field;
  72      var $lasteditor_field;
  73      var $dbchanges = array();
  74      /**#@-*/

  75  
  76      /**

  77       * Relations that may restrict deletion.

  78       */
  79      var $delete_restrictions = array();
  80  
  81      /**

  82       * Relations that will cascade deletion.

  83       */
  84      var $delete_cascades = array();
  85  
  86  
  87      /**

  88       * Constructor

  89       *

  90       * @param string Name of table in database

  91       * @param string Prefix of fields in the table

  92       * @param string Name of the ID field (including prefix)

  93       * @param string datetime field name

  94       * @param string datetime field name

  95       * @param string User ID field name

  96       * @param string User ID field name

  97       */
  98  	function DataObject( $tablename, $prefix = '', $dbIDname = 'ID', $datecreated_field = '', $datemodified_field = '', $creator_field = '', $lasteditor_field = '' )
  99      {
 100          $this->dbtablename        = $tablename;
 101          $this->dbprefix           = $prefix;
 102          $this->dbIDname           = $dbIDname;
 103          $this->datecreated_field  = $datecreated_field;
 104          $this->datemodified_field = $datemodified_field;
 105          $this->creator_field      = $creator_field;
 106          $this->lasteditor_field   = $lasteditor_field;
 107      }
 108  
 109  
 110      /**

 111       * Records a change that will need to be updated in the db

 112       *

 113       * @access protected

 114       * @param string Name of parameter

 115       * @param string DB field type ('string', 'number', 'date' )

 116       * @param mixed Pointer to value of parameter - dh> pointer? So it should be a reference? Would make sense IMHO anyway.. fp> I just wonder why it's not already a reference... :@ 

 117       */
 118  	function dbchange( $dbfieldname, $dbfieldtype, $valuepointer ) // TODO: dh> value by reference? see above..
 119      {
 120          // echo '<br />DB change on :'.$dbfieldname;

 121          $this->dbchanges[$dbfieldname]['type'] = $dbfieldtype;
 122          $this->dbchanges[$dbfieldname]['value'] = $valuepointer ;
 123      }
 124  
 125  
 126      /**

 127       * Update the DB based on previously recorded changes

 128       *

 129       * @return boolean true on success, false on failure to update, NULL if no update necessary

 130       */
 131  	function dbupdate()
 132      {
 133          global $DB, $localtimenow, $current_User;
 134  
 135          if( $this->ID == 0 ) { debug_die( 'New object cannot be updated!' ); }
 136  
 137          if( count( $this->dbchanges ) == 0 )
 138          {
 139              return NULL;    // No changes!

 140          }
 141  
 142          if( !empty($this->datemodified_field) )
 143          {    // We want to track modification date:
 144              $this->set_param( $this->datemodified_field, 'date', date('Y-m-d H:i:s',$localtimenow) );
 145          }
 146          if( !empty($this->lasteditor_field) && is_object($current_User) )
 147          {    // We want to track last editor:
 148              // TODO: the current_User is not necessarily the last editor. Item::dbupdate() gets called after incrementing the view for example!

 149              // fplanque: this should be handled by set() deciding wether the setting changes the last editor or not

 150              $this->set_param( $this->lasteditor_field, 'number', $current_User->ID );
 151          }
 152  
 153  
 154          $sql_changes = array();
 155          foreach( $this->dbchanges as $loop_dbfieldname => $loop_dbchange )
 156          {
 157              // Get changed value (we use eval() to allow constructs like $loop_dbchange['value'] = 'Group->get(\'ID\')'):

 158              eval( '$loop_value = $this->'.$loop_dbchange['value'].';' );
 159              // Prepare matching statement:

 160              if( is_null($loop_value) )
 161              {
 162                  $sql_changes[] = $loop_dbfieldname.' = NULL ';
 163              }
 164              else
 165              {
 166                  switch( $loop_dbchange['type'] )
 167                  {
 168                      case 'date':
 169                      case 'string':
 170                          $sql_changes[] = $loop_dbfieldname." = '".$DB->escape( $loop_value )."' ";
 171                          break;
 172  
 173                      default:
 174                          $sql_changes[] = $loop_dbfieldname." = ".$DB->null($loop_value).' ';
 175                  }
 176              }
 177          }
 178  
 179          // Prepare full statement:

 180          $sql = "UPDATE $this->dbtablename SET ". implode( ', ', $sql_changes ). "
 181                           WHERE $this->dbIDname = $this->ID";
 182          //echo $sql;

 183  
 184          if( ! $DB->query( $sql, 'DataObject::dbupdate()' ) )
 185          {
 186              return false;
 187          }
 188  
 189          // Reset changes in object:

 190          $this->dbchanges = array();
 191  
 192          return true;
 193      }
 194  
 195  
 196      /**

 197       * Insert object into DB based on previously recorded changes.

 198       *

 199       * @return boolean true on success

 200       */
 201  	function dbinsert()
 202      {
 203          global $DB, $localtimenow, $current_User;
 204  
 205          if( $this->ID != 0 ) die( 'Existing object cannot be inserted!' );
 206  
 207          if( !empty($this->datecreated_field) )
 208          {    // We want to track creation date:
 209              $this->set_param( $this->datecreated_field, 'date', date('Y-m-d H:i:s',$localtimenow) );
 210          }
 211          if( !empty($this->datemodified_field) )
 212          {    // We want to track modification date:
 213              $this->set_param( $this->datemodified_field, 'date', date('Y-m-d H:i:s',$localtimenow) );
 214          }
 215          if( !empty($this->creator_field) )
 216          {    // We want to track creator:
 217              if( empty($this->creator_user_ID) )
 218              {    // No creator assigned yet, use current user:
 219                  $this->set_param( $this->creator_field, 'number', $current_User->ID );
 220              }
 221          }
 222          if( !empty($this->lasteditor_field) )
 223          {    // We want to track last editor:
 224              if( empty($this->lastedit_user_ID) )
 225              {    // No editor assigned yet, use current user:
 226                  $this->set_param( $this->lasteditor_field, 'number', $current_User->ID );
 227              }
 228          }
 229  
 230          $sql_fields = array();
 231          $sql_values = array();
 232          foreach( $this->dbchanges as $loop_dbfieldname => $loop_dbchange )
 233          {
 234              // Get changed value (we use eval() to allow constructs like $loop_dbchange['value'] = 'Group->get(\'ID\')'):

 235              eval( '$loop_value = $this->'. $loop_dbchange['value'].';' );
 236              // Prepare matching statement:

 237              $sql_fields[] = $loop_dbfieldname;
 238              if( is_null($loop_value) )
 239              {
 240                  $sql_values[] = 'NULL';
 241              }
 242              else
 243              {
 244                  switch( $loop_dbchange['type'] )
 245                  {
 246                      case 'date':
 247                      case 'string':
 248                          $sql_values[] = $DB->quote( $loop_value );
 249                          break;
 250  
 251                      default:
 252                          $sql_values[] = $DB->null( $loop_value );
 253                  }
 254              }
 255          }
 256  
 257          // Prepare full statement:

 258          $sql = "INSERT INTO {$this->dbtablename} (". implode( ', ', $sql_fields ). ") VALUES (". implode( ', ', $sql_values ). ")";
 259          // echo $sql;

 260  
 261          if( ! $DB->query( $sql, 'DataObject::dbinsert()' ) )
 262          {
 263              return false;
 264          }
 265  
 266          // store ID for newly created db record

 267          $this->ID = $DB->insert_id;
 268  
 269          // Reset changes in object:

 270          $this->dbchanges = array();
 271  
 272          return true;
 273      }
 274  
 275  
 276      /**

 277       * Inserts or Updates depending on object state.

 278       *

 279       * @uses dbinsert()

 280       * @uses dbupdate()

 281       * @return boolean true on success, false on failure

 282       */
 283  	function dbsave()
 284      {
 285          if( $this->ID == 0 )
 286          {    // Object not serialized yet, let's insert!
 287              // echo 'INSERT';

 288              return $this->dbinsert();
 289          }
 290          else
 291          {    // Object already serialized, let's update!
 292              // echo 'UPDATE';

 293              return $this->dbupdate();
 294          }
 295      }
 296  
 297  
 298      /**

 299       * Delete object from DB.

 300       *

 301       * @return boolean true on success

 302       */
 303  	function dbdelete()
 304      {
 305          global $DB, $Messages, $db_config;
 306  
 307          if( $this->ID == 0 ) { debug_die( 'Non persistant object cannot be deleted!' ); }
 308  
 309          if( count($this->delete_cascades) )
 310          {    // The are cascading deletes to be performed
 311  
 312              // Start transaction:

 313              $DB->begin();
 314  
 315              foreach( $this->delete_cascades as $restriction )
 316              {
 317                  if( !isset( $db_config['aliases'][$restriction['table']] ) )
 318                  {    // We have no declaration for this table, we consider we don't deal with this table in this app:
 319                      continue;
 320                  }
 321  
 322                  $DB->query( '
 323                      DELETE FROM '.$restriction['table'].'
 324                      WHERE '.$restriction['fk'].' = '.$this->ID,
 325                      'Cascaded delete' );
 326              }
 327          }
 328  
 329          // Delete this (main/parent) object:

 330          $DB->query( "
 331              DELETE FROM $this->dbtablename
 332              WHERE $this->dbIDname = $this->ID",
 333              'Main delete' );
 334  
 335          if( count($this->delete_cascades) )
 336          {    // There were cascading deletes
 337  
 338              // End transaction:

 339              $DB->commit();
 340          }
 341  
 342          // Just in case... remember this object has been deleted from DB!

 343          $this->ID = 0;
 344  
 345          return true;
 346      }
 347  
 348  
 349      /**

 350       * Check relations for restrictions or cascades

 351       */
 352  	function check_relations( $what, $ignore = array() )
 353      {
 354          global $DB, $Messages;
 355  
 356          foreach( $this->$what as $restriction )
 357          {
 358              if( !in_array( $restriction['fk'], $ignore ) )
 359              {
 360                  $count = $DB->get_var(
 361                      'SELECT COUNT(*)
 362                         FROM '.$restriction['table'].'
 363                        WHERE '.$restriction['fk'].' = '.$this->ID,
 364                      0, 0, 'restriction/cascade check' );
 365                  if( $count )
 366                  {
 367                      $Messages->add( sprintf( $restriction['msg'], $count ), 'restrict' );
 368                  }
 369              }
 370          }
 371      }
 372  
 373  
 374      /**

 375       * Check relations for restrictions before deleting

 376       *

 377       * @param string

 378       * @param array list of foreign keys to ignore

 379       * @return boolean true if no restriction prevents deletion

 380       */
 381  	function check_delete( $restrict_title, $ignore = array() )
 382      {
 383          global $Messages;
 384  
 385          // Check restrictions:

 386          $this->check_relations( 'delete_restrictions', $ignore );
 387  
 388          if( $Messages->count('restrict') )
 389          {    // There are restrictions:
 390              $Messages->head = array(
 391                      'container' => $restrict_title,
 392                      'restrict' => T_('The following relations prevent deletion:')
 393                  );
 394              $Messages->foot =    T_('Please delete related objects before you proceed.');
 395              return false;    // Can't delete

 396          }
 397  
 398          return true;    // can delete

 399      }
 400  
 401  
 402      /**

 403       * Displays form to confirm deletion of this object

 404       *

 405       * @param string Title for confirmation

 406       * @param string "action" param value to use (hidden field)

 407       * @param array Hidden keys (apart from "action")

 408       * @param string most of the time we don't need a cancel action since we'll want to return to the default display

 409       */
 410  	function confirm_delete( $confirm_title, $delete_action, $hiddens, $cancel_action = NULL )
 411      {
 412          global $Messages;
 413  
 414          // No restrictions, ask for confirmation:

 415          echo '<div class="panelinfo">';
 416          echo '<h2>'.$confirm_title.'</h2>';
 417  
 418          $this->check_relations( 'delete_cascades' );
 419  
 420          if( $Messages->count('restrict') )
 421          {    // The will be cascading deletes, issue WARNING:
 422              echo '<h3>'.T_('WARNING: Deleting this object will also delete:').'</h3>';
 423              $Messages->display( '', '', true, 'restrict', NULL, NULL, NULL );
 424          }
 425  
 426          echo '<h3>'.T_('THIS CANNOT BE UNDONE!').'</h3>';
 427  
 428          $Form = & new Form( '', 'form_confirm', 'get', '' );
 429  
 430          $Form->begin_form( 'inline' );
 431              $Form->hiddens_by_key( $hiddens );
 432              $Form->hidden( 'action', $delete_action );
 433              $Form->hidden( 'confirm', 1 );
 434              $Form->button( array( 'submit', '', T_('I am sure!'), 'DeleteButton' ) );
 435          $Form->end_form();
 436  
 437          $Form = & new Form( '', 'form_cancel', 'get', '' );
 438  
 439          $Form->begin_form( 'inline' );
 440              $Form->hiddens_by_key( $hiddens );
 441              if( !empty( $cancel_action ) )
 442              {
 443                  $Form->hidden( 'action', $cancel_action );
 444              }
 445              $Form->button( array( 'submit', '', T_('CANCEL'), 'CancelButton' ) );
 446          $Form->end_form();
 447  
 448          echo '</div>';
 449          return true;
 450      }
 451  
 452  
 453      /**

 454       * Get a member param by its name

 455       *

 456       * @param mixed Name of parameter

 457       * @return mixed Value of parameter

 458       */
 459  	function get( $parname )
 460      {
 461          return $this->$parname;
 462      }
 463  
 464  
 465      /**

 466       * Get a ready-to-display member param by its name

 467       *

 468       * Same as disp but don't echo

 469       *

 470       * @param string Name of parameter

 471       * @param string Output format, see {@link format_to_output()}

 472       */
 473  	function dget( $parname, $format = 'htmlbody' )
 474      {
 475          // Note: we call get again because of derived objects specific handlers !

 476          return format_to_output( $this->get($parname), $format );
 477      }
 478  
 479  
 480      /**

 481       * Display a member param by its name

 482       *

 483       * @param string Name of parameter

 484       * @param string Output format, see {@link format_to_output()}

 485       */
 486  	function disp( $parname, $format = 'htmlbody' )
 487      {
 488          // Note: we call get again because of derived objects specific handlers !

 489          echo format_to_output( $this->get($parname), $format );
 490      }
 491  
 492  
 493      /**

 494       * Set param value

 495       *

 496       * By default, all values will be considered strings

 497       *

 498       * @param string parameter name

 499       * @param mixed parameter value

 500       * @param boolean true to set to NULL if empty value

 501       * @return boolean true, if a value has been set; false if it has not changed

 502       */
 503  	function set( $parname, $parvalue, $make_null = false )
 504      {
 505          return $this->set_param( $parname, 'string', $parvalue, $make_null );
 506      }
 507  
 508  
 509      /**

 510       * Set param value.

 511       *

 512       * @param string Name of parameter

 513       * @param string DB field type ('string', 'number', 'date' )

 514       * @param mixed Value of parameter

 515       * @param boolean true to set to NULL if empty string value

 516       * @return boolean true, if value has been set/changed, false if not.

 517       */
 518  	function set_param( $parname, $fieldtype, $parvalue, $make_null = false )
 519      {
 520          global $Debuglog;
 521  
 522          $dbfield = $this->dbprefix.$parname;
 523  
 524          // Set value:

 525          // fplanque: Note: I am changing the "make NULL" test to differentiate between 0 and NULL .

 526          // There might be side effects. In this case it would be better to fix them before coming here.

 527          // i-e: transform 0 to ''

 528          $new_value = ($make_null && ($parvalue === '')) ? NULL : $parvalue;
 529  
 530  /* >old

 531          if( !isset($this->$parname) )

 532          {    // This property has never been set before, set it to NULL now in order for tests to work:

 533              $this->$parname = NULL;

 534          }

 535  

 536  

 537          /* blueyed>

 538          TODO: there's a bug here: you cannot use set_param('foo', 'number', 0), if the $parname member

 539                has not been set before or is null!!

 540                What about just:

 541                ( isset($this->$parname) && $this->$parname === $new_value )

 542                This would also eliminate the isset() check from above.

 543                IIRC you've once said here that '===' would be too expensive and I would misuse the DataObjects,

 544                but IMHO what we have now is not much faster and buggy anyway..

 545              fp> okay let's give it a try...

 546          if( (!is_null($new_value) && $this->$parname == $new_value)

 547              || (is_null($this->$parname) && is_null($new_value)) )

 548  <old */
 549          if( (isset($this->$parname) && $this->$parname === $new_value)
 550              || ( ! isset($this->$parname) && ! isset($new_value) ) )
 551          {    // Value has not changed (we need 2 tests, for NULL and for NOT NULL value pairs)
 552              $Debuglog->add( $this->dbtablename.' object, already set to same value: '.$parname.'/'.$dbfield.' = '.var_export( @$this->$parname, true ), 'dataobjects' );
 553              // echo '<br />'.$this->dbtablename.' object, already set to same value: '.$parname.'/'.$dbfield.' = '.$this->$parname;

 554  
 555              return false;
 556          }
 557          else
 558          {
 559              // Set the value in the object:

 560              // echo '<br/>'.$this->dbtablename.' object, setting param '.$parname.'/'.$dbfield.' to '.$new_value.(is_null($new_value)?' NULL':'').' (was:'.$this->$parname.(is_null($this->$parname)?' NULL':'').')';

 561              $this->$parname = $new_value;
 562              $Debuglog->add( $this->dbtablename.' object, setting param '.$parname.'/'.$dbfield.' to '.$this->$parname, 'dataobjects' );
 563  
 564              // Remember change for later db update:

 565              $this->dbchange( $dbfield, $fieldtype, $parname );
 566  
 567              return true;
 568          }
 569      }
 570  
 571  
 572      /**

 573       * Set a parameter from a Request form value.

 574       *

 575       * @param string Dataobject parameter name

 576       * @param string Request parameter name (NULL means to use Dataobject param name with its prefix)

 577       * @param boolean true to set to NULL if empty string value

 578       * @return boolean true, if value has been set/changed, false if not.

 579       */
 580  	function set_from_Request( $parname, $var = NULL, $make_null = false )
 581      {
 582          if( empty($var) )
 583          {
 584              $var = $this->dbprefix.$parname;
 585          }
 586  
 587          return $this->set( $parname, get_param($var), $make_null );
 588      }
 589  
 590  
 591      /**

 592       * Template function: Displays object ID.

 593       */
 594      function ID()
 595      {
 596          echo $this->ID;
 597      }
 598  
 599      /**

 600       * Create icon with dataobject history

 601       */
 602  	function history_info_icon()
 603      {
 604          $history = array();
 605  
 606          $UserCache = & get_Cache( 'UserCache' );
 607  
 608          // HANDLE CREATOR STUFF

 609          if( !empty($this->creator_field) && !empty($this->{$this->creator_field}) )
 610          {    // We have a creator:
 611              $creator_User = & $UserCache->get_by_ID( $this->{$this->creator_field} );
 612  
 613              if( !empty($this->datecreated_field) && !empty($this->{$this->datecreated_field}) )
 614              {    // We also have a create date:
 615                  $history[0] = sprintf( T_('Created on %s by %s'), mysql2localedate( $this->{$this->datecreated_field} ),
 616                      $creator_User->dget('preferredname') );
 617              }
 618              else
 619              {    // We only have a cretaor:
 620                  $history[0] = sprintf( T_('Created by %s'), $creator_User->dget('preferredname') );
 621              }
 622          }
 623          elseif( !empty($this->datecreated_field) && !empty($this->{$this->datecreated_field}) )
 624          {    // We only have a create date:
 625              $history[0] = sprintf( T_('Created on %s'), mysql2localedate( $this->{$this->datecreated_field} ) );
 626          }
 627  
 628          // HANDLE LAST UPDATE STUFF

 629          if( !empty($this->lasteditor_field) && !empty($this->{$this->lasteditor_field}) )
 630          {    // We have a creator:
 631              $creator_User = & $UserCache->get_by_ID( $this->{$this->lasteditor_field} );
 632  
 633              if( !empty($this->datemodified_field) && !empty($this->{$this->datemodified_field}) )
 634              {    // We also have a create date:
 635                  $history[1] = sprintf( T_('Last mod on %s by %s'), mysql2localedate( $this->{$this->datemodified_field} ),
 636                      $creator_User->dget('preferredname') );
 637              }
 638              else
 639              {    // We only have a cretaor:
 640                  $history[1] = sprintf( T_('Last mod by %s'), $creator_User->dget('preferredname') );
 641              }
 642          }
 643          elseif( !empty($this->datemodified_field) && !empty($this->{$this->datemodified_field}) )
 644          {    // We only have a create date:
 645              $history[1] = sprintf( T_('Last mod on %s'), mysql2localedate( $this->{$this->datemodified_field} ) );
 646          }
 647  
 648          return get_icon( 'history', $what = 'imgtag', array( 'title'=>implode( ' - ', $history ) ), true );
 649      }
 650  }
 651  
 652  
 653  
 654  /*

 655   * $Log: _dataobject.class.php,v $

 656   * Revision 1.1  2007/06/25 10:58:56  fplanque

 657   * MODULES (refactored MVC)

 658   *

 659   * Revision 1.26  2007/05/14 02:43:04  fplanque

 660   * Started renaming tables. There probably won't be a better time than 2.0.

 661   *

 662   * Revision 1.25  2007/05/13 22:02:07  fplanque

 663   * removed bloated $object_def

 664   *

 665   * Revision 1.24  2007/04/26 00:11:09  fplanque

 666   * (c) 2007

 667   *

 668   * Revision 1.23  2007/01/07 23:37:26  fplanque

 669   * doc cleanup

 670   *

 671   * Revision 1.22  2006/12/10 23:20:55  fplanque

 672   * hum...

 673   *

 674   * Revision 1.21  2006/12/10 03:04:31  blueyed

 675   * todo

 676   *

 677   * Revision 1.20  2006/11/24 18:27:24  blueyed

 678   * Fixed link to b2evo CVS browsing interface in file docblocks

 679   */
 680  ?>


Généré le : Thu Nov 29 23:58:50 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics