[ Index ]
 

Code source de eGroupWare 1.2.106-2

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/etemplate/inc/ -> class.db_tools.inc.php (source)

   1  <?php
   2      /**************************************************************************\
   3      * eGroupWare - eTemplates - DB-Tools                                       *
   4      * http://www.egroupware.org                                                *
   5      * Written by Ralf Becker <RalfBecker@outdoor-training.de>                  *
   6      * --------------------------------------------                             *
   7      *  This program is free software; you can redistribute it and/or modify it *
   8      *  under the terms of the GNU General Public License as published by the   *
   9      *  Free Software Foundation; either version 2 of the License, or (at your  *
  10      *  option) any later version.                                              *
  11      \**************************************************************************/
  12  
  13      /* $Id: class.db_tools.inc.php 20167 2005-12-19 04:27:19Z ralfbecker $ */
  14  
  15      /**
  16       * db-tools: creats and modifys eGroupWare schem-files (to be installed via setup)
  17       *
  18       * @package etemplate
  19       * @subpackage tools
  20       * @author RalfBecker-AT-outdoor-training.de
  21       * @license GPL
  22       */
  23      class db_tools
  24      {
  25          var $public_functions = array
  26          (
  27              'edit'         => True,
  28              'needs_save'   => True,
  29          );
  30  
  31          var $debug = 0;
  32          var $editor;    // editor eTemplate
  33          var $data;        // Table definitions
  34          var $app;        // used app
  35          var $table;        // used table
  36          var $types = array(
  37              'varchar'    => 'varchar',
  38              'int'        => 'int',
  39              'auto'        => 'auto',
  40              'blob'        => 'blob',
  41              'char'        => 'char',
  42              'date'        => 'date',
  43              'decimal'    => 'decimal',
  44              'float'        => 'float',
  45              'longtext'    => 'longtext',
  46              'text'        => 'text',
  47              'timestamp'    => 'timestamp',
  48              'bool'      => 'boolean',
  49  //            'abstime'   => 'abstime (mysql:timestamp)',
  50          );
  51          var $setup_header = '<?php
  52      /**************************************************************************\\
  53      * eGroupWare - Setup                                                       *
  54      * http://www.eGroupWare.org                                                *
  55      * Created by eTemplates DB-Tools written by ralfbecker@outdoor-training.de *
  56      * --------------------------------------------                             *
  57      * This program is free software; you can redistribute it and/or modify it  *
  58      * under the terms of the GNU General Public License as published by the    *
  59      * Free Software Foundation; either version 2 of the License, or (at your   *
  60      * option) any later version.                                               *
  61      \\**************************************************************************/
  62      
  63      /* $Id: class.db_tools.inc.php 20167 2005-12-19 04:27:19Z ralfbecker $ */
  64  ';
  65  
  66          /**
  67           * constructor of class
  68           */
  69  		function db_tools()
  70          {
  71              $this->editor =& CreateObject('etemplate.etemplate','etemplate.db-tools.edit');
  72              $this->data = array();
  73  
  74              if (!is_array($GLOBALS['egw_info']['apps']) || !count($GLOBALS['egw_info']['apps']))
  75              {
  76                  ExecMethod('phpgwapi.applications.read_installed_apps');
  77              }
  78              $GLOBALS['egw_info']['flags']['app_header'] =
  79                  $GLOBALS['egw_info']['apps']['etemplate']['title'].' - '.lang('DB-Tools');
  80          }
  81  
  82          /**
  83           * table editor (and the callback/submit-method too)
  84           */
  85  		function edit($content='',$msg = '')
  86          {
  87              if (isset($_GET['app']))
  88              {
  89                  $this->app = $_GET['app'];
  90              }
  91              if (is_array($content))
  92              {
  93                  if ($this->debug)
  94                  {
  95                      echo "content ="; _debug_array($content);
  96                  }
  97                  $this->app = $content['app'];    // this is what the user selected
  98                  $this->table = $content['table_name'];
  99                  $posted_app = $content['posted_app'];    // this is the old selection
 100                  $posted_table = $content['posted_table'];
 101              }
 102              if ($posted_app && $posted_table &&        // user changed app or table
 103                   ($posted_app != $this->app || $posted_table != $this->table))
 104              {
 105                  if ($this->needs_save('',$posted_app,$posted_table,$this->content2table($content)))
 106                  {
 107                      return;
 108                  }
 109                  $this->renames = array();
 110              }
 111              if (!$this->app)
 112              {
 113                  $this->table = '';
 114                  $table_names = array('' => lang('none'));
 115              }
 116              else
 117              {
 118                  $this->read($this->app,$this->data);
 119  
 120                  foreach($this->data as $name => $table)
 121                  {
 122                      $table_names[$name] = $name;
 123                  }
 124              }
 125              if (!$this->table || $this->app != $posted_app)
 126              {
 127                  reset($this->data);
 128                  list($this->table) = each($this->data);    // use first table
 129              }
 130              elseif ($this->app == $posted_app && $posted_table)
 131              {
 132                  $this->data[$posted_table] = $this->content2table($content);
 133              }
 134              if ($content['write_tables'])
 135              {
 136                  if ($this->needs_save('',$this->app,$this->table,$this->data[$posted_table]))
 137                  {
 138                      return;
 139                  }
 140                  $msg .= lang('Table unchanged, no write necessary !!!');
 141              }
 142              elseif ($content['delete'])
 143              {
 144                  list($col) = each($content['delete']);
 145  
 146                  @reset($this->data[$posted_table]['fd']);
 147                  while ($col-- > 0 && list($key,$data) = @each($this->data[$posted_table]['fd'])) ;
 148  
 149                  unset($this->data[$posted_table]['fd'][$key]);
 150                  $this->changes[$posted_table][$key] = '**deleted**';
 151              }
 152              elseif ($content['add_column'])
 153              {
 154                  $this->data[$posted_table]['fd'][''] = array();
 155              }
 156              elseif ($content['add_table'] || $content['import'])
 157              {
 158                  if (!$this->app)
 159                  {
 160                      $msg .= lang('Select an app first !!!');
 161                  }
 162                  elseif (!$content['new_table_name'])
 163                  {
 164                      $msg .= lang('Please enter table-name first !!!');
 165                  }
 166                  elseif ($content['add_table'])
 167                  {
 168                      $this->table = $content['new_table_name'];
 169                      $this->data[$this->table] = array('fd' => array(),'pk' =>array(),'ix' => array(),'uc' => array(),'fk' => array());
 170                      $msg .= lang('New table created');
 171                  }
 172                  else // import
 173                  {
 174                      $oProc =& CreateObject('phpgwapi.schema_proc',$GLOBALS['egw_info']['server']['db_type']);
 175                      if (method_exists($oProc,'GetTableDefinition'))
 176                      {
 177                          $this->data[$this->table = $content['new_table_name']] = $oProc->GetTableDefinition($content['new_table_name']);
 178                      }
 179                      else    // to support eGW 1.0
 180                      {
 181                          $oProc->m_odb = clone($GLOBALS['egw']->db);
 182                          $oProc->m_oTranslator->_GetColumns($oProc,$content['new_table_name'],$nul);
 183      
 184                          while (list($key,$tbldata) = each ($oProc->m_oTranslator->sCol))
 185                          {
 186                              $cols .= $tbldata;
 187                          }
 188                          eval('$cols = array('. $cols . ');');
 189      
 190                          $this->data[$this->table = $content['new_table_name']] = array(
 191                              'fd' => $cols,
 192                              'pk' => $oProc->m_oTranslator->pk,
 193                              'fk' => $oProc->m_oTranslator->fk,
 194                              'ix' => $oProc->m_oTranslator->ix,
 195                              'uc' => $oProc->m_oTranslator->uc
 196                          );
 197                      }
 198                  }
 199              }
 200              elseif ($content['editor'])
 201              {
 202                  ExecMethod('etemplate.editor.edit');
 203                  return;
 204              }
 205              $add_index = isset($content['add_index']);
 206  
 207              // from here on, filling new content for eTemplate
 208              $content = array(
 209                  'msg' => $msg,
 210                  'table_name' => $this->table,
 211                  'app' => $this->app,
 212              );
 213              if (!isset($table_names[$this->table]))    // table is not jet written
 214              {
 215                  $table_names[$this->table] = $this->table;
 216              }
 217              $sel_options = array(
 218                  'table_name' => $table_names,
 219                  'type' => $this->types
 220              );
 221              if ($this->table != '' && isset($this->data[$this->table]))
 222              {
 223                  $content += $this->table2content($this->data[$this->table],$sel_options['Index'],$add_index);
 224              }
 225              $no_button = array( );
 226              if (!$this->app || !$this->table)
 227              {
 228                  $no_button += array('write_tables' => True);
 229              }
 230              if ($this->debug)
 231              {
 232                  echo 'editor.edit: content ='; _debug_array($content);
 233              }
 234              $this->editor->exec('etemplate.db_tools.edit',$content,$sel_options,$no_button,
 235                  array('posted_table' => $this->table,'posted_app' => $this->app,'changes' => $this->changes));
 236          }
 237  
 238          /**
 239           * checks if table was changed and if so offers user to save changes
 240           *
 241           * @param array $cont the content of the form (if called by process_exec)
 242           * @param string $posted_app the app the table is from
 243           * @param string $posted_table the table-name
 244           * @param array $edited_table the edited table-definitions
 245           * @return only if no changes
 246           */
 247  		function needs_save($cont='',$posted_app='',$posted_table='',$edited_table='',$msg='')
 248          {
 249              //echo "<p>db_tools::needs_save(cont,'$posted_app','$posted_table',edited_table,'$msg')</p> cont=\n"; _debug_array($cont); echo "edited_table="; _debug_array($edited_table);
 250              if (!$posted_app && is_array($cont))
 251              {
 252                  if (isset($cont['yes']))
 253                  {
 254                      $this->app   = $cont['app'];
 255                      $this->table = $cont['table'];
 256                      $this->read($this->app,$this->data);
 257                      $this->data[$this->table] = $cont['edited_table'];
 258                      $this->changes = $cont['changes'];
 259                      if ($cont['new_version'])
 260                      {
 261                          $this->update($this->app,$this->data,$cont['new_version']);
 262                      }
 263                      else
 264                      {
 265                          foreach($this->data as $tname => $tinfo)
 266                          {
 267                              $tables .= ($tables ? ',' : '') . "'$tname'";
 268                          }
 269                          $this->setup_version($this->app,'',$tables);
 270                      }
 271                      if (!$this->write($this->app,$this->data))
 272                      {
 273                          $this->app = $cont['new_app'];    // these are the ones, the users whiches to change too
 274                          $this->table = $cont['new_table'];
 275  
 276                          return $this->needs_save('',$cont['app'],$cont['table'],$cont['edited_table'],
 277                              lang('Error: writing file (no write-permission for the webserver) !!!'));
 278                      }
 279                      $msg = lang('File writen');
 280                  }
 281                  $this->changes = array();
 282                  // return to edit with everything set, so the user gets the table he asked for
 283                  $this->edit(array(
 284                      'app' => $cont['new_app'],
 285                      'table_name' => $cont['app']==$cont['new_app'] ? $cont['new_table'] : '',
 286                      'posted_app' => $cont['new_app']
 287                  ),$msg);
 288  
 289                  return True;
 290              }
 291              $new_app   = $this->app;    // these are the ones, the users whiches to change too
 292              $new_table = $this->table;
 293  
 294              $this->app = $posted_app;
 295              $this->data = array();
 296              $this->read($posted_app,$this->data);
 297  
 298              if (isset($this->data[$posted_table]) &&
 299                   $this->tables_identical($this->data[$posted_table],$edited_table))
 300              {
 301                  if ($new_app != $this->app)    // are we changeing the app, or hit the user just write
 302                  {
 303                      $this->app = $new_app;    // if we change init the data empty
 304                      $this->data = array();
 305                  }
 306                  return False;    // continue edit
 307              }
 308              $content = array(
 309                  'msg' => $msg,
 310                  'app' => $posted_app,
 311                  'table' => $posted_table,
 312                  'version' => $this->setup_version($posted_app)
 313              );
 314              $preserv = $content + array(
 315                  'new_app' => $new_app,
 316                  'new_table' => $new_table,
 317                  'edited_table' => $edited_table,
 318                  'changes' => $this->changes
 319              );
 320              $new_version = explode('.',$content['version']);
 321              $minor = count($new_version)-1;
 322              $new_version[$minor] = sprintf('%03d',1+$new_version[$minor]);
 323              $content['new_version'] = implode('.',$new_version);
 324  
 325              $tmpl =& new etemplate('etemplate.db-tools.ask_save');
 326  
 327              if (!file_exists(EGW_SERVER_ROOT."/$posted_app/setup/tables_current.inc.php"))
 328              {
 329                  $tmpl->disable_cells('version');
 330                  $tmpl->disable_cells('new_version');
 331              }
 332              $tmpl->exec('etemplate.db_tools.needs_save',$content,array(),array(),$preserv);
 333  
 334              return True;    // dont continue in edit
 335          }
 336  
 337          /**
 338           * checks if there is an index (only) on $col (not a multiple index incl. $col)
 339           *
 340           * @param string $col column name
 341           * @param array $index ix or uc array of table-defintion
 342           * @param string &$options db specific options
 343           * @return True if $col has a single index
 344           */
 345  		function has_single_index($col,$index,&$options)
 346          {
 347              foreach($index as $in)
 348              {
 349                  if ($in == $col || is_array($in) && $in[0] == $col && !isset($in[1]))
 350                  {
 351                      if ($in != $col && isset($in['options']))
 352                      {
 353                          foreach($in['options'] as $db => $opts)
 354                          {
 355                              $options[] = $db.'('.(is_array($opts)?implode(',',$opts):$opts).')';
 356                          }
 357                          $options = implode(', ',$options);
 358                      }
 359                      return True;
 360                  }
 361              }
 362              return False;
 363          }
 364  
 365          /**
 366           * creates content-array from a table
 367           *
 368           * @param array $table table-definition, eg. $phpgw_baseline[$table_name]
 369           * @param array &$columns returns array with column-names
 370           * @param bool $extra_index add an additional index-row
 371           * @return array content-array to call exec with
 372           */
 373  		function table2content($table,&$columns,$extra_index=False)
 374          {
 375              $content = $columns = array();
 376              for ($n = 1; list($col_name,$col_defs) = each($table['fd']); ++$n)
 377              {
 378                  $col_defs['name'] = $col_name;
 379                  $col_defs['pk'] = in_array($col_name,$table['pk']);
 380                  $col_defs['uc']  = $this->has_single_index($col_name,$table['uc'],$col_defs['options']);
 381                  $col_defs['ix'] = $this->has_single_index($col_name,$table['ix'],$col_defs['options']);
 382                  $col_defs['fk'] = $table['fk'][$col_name];
 383                  if (isset($col_defs['default']) && $col_defs['default'] == '')
 384                  {
 385                      $col_defs['default'] = is_int($col_defs['default']) ? '0' : "''";    // spezial value for empty, but set, default
 386                  }
 387                  $col_defs['notnull'] = isset($col_defs['nullable']) && !$col_defs['nullable'];
 388  
 389                  $col_defs['n'] = $n;
 390  
 391                  $content["Row$n"] = $col_defs;
 392  
 393                  $columns[$n] = $col_name;
 394              }
 395              $n = 2;
 396              foreach(array('uc','ix') as $type)
 397              {
 398                  foreach($table[$type] as $index)
 399                  {
 400                      if (is_array($index) && isset($index[1]))    // multicolum index
 401                      {
 402                          $content['Index'][$n]['unique'] = $type == 'uc';
 403                          $content['Index'][$n]['n'] = $n - 1;
 404                          foreach($index as $col)
 405                          {
 406                              $content['Index'][$n][] = array_search($col,$columns);
 407                          }
 408                          ++$n;
 409                      }
 410                  }
 411              }
 412              if ($extra_index)
 413              {
 414                  $content['Index'][$n]['n'] = $n-1;
 415              }
 416              if ($this->debug >= 3)
 417              {
 418                  echo "<p>table2content(,,'$extra_index'): content ="; _debug_array($content);
 419                  echo "<p>columns ="; _debug_array($columns);
 420              }
 421              return $content;
 422          }
 423  
 424          /**
 425           * creates table-definition from posted content
 426           *
 427           * It sets some reasonalbe defaults for not set precisions (else setup will not install)
 428           *
 429           * @param array $content posted content-array
 430           * @return table-definition
 431           */
 432  		function content2table($content)
 433          {
 434              if (!is_array($this->data))
 435              {
 436                  $this->read($content['posted_app'],$this->data);
 437              }
 438              $old_cols = $this->data[$posted_table = $content['posted_table']]['fd'];
 439              $this->changes = $content['changes'];
 440  
 441              $table = array();
 442              $table['fd'] = array();    // do it in the default order of tables_*
 443              $table['pk'] = array();
 444              $table['fk'] = array();
 445              $table['ix'] = array();
 446              $table['uc'] = array();
 447              for (reset($content),$n = 1; isset($content["Row$n"]); ++$n)
 448              {
 449                  $col = $content["Row$n"];
 450  
 451                  while ((list($old_name,$old_col) = @each($old_cols)) &&
 452                               $this->changes[$posted_table][$old_name] == '**deleted**') ;
 453  
 454                  if (($name = $col['name']) != '')        // ignoring lines without column-name
 455                  {
 456                      if ($col['name'] != $old_name && $n <= count($old_cols))    // column renamed --> remeber it
 457                      {
 458                          $this->changes[$posted_table][$old_name] = $col['name'];
 459                          //echo "<p>content2table: $posted_table.$old_name renamed to $col[name]</p>\n";
 460                      }
 461                      if ($col['precision'] <= 0)
 462                      {
 463                          switch ($col['type']) // set some defaults for precision, else setup fails
 464                          {
 465                              case 'float':
 466                              case 'int':     $col['precision'] = 4; break;
 467                              case 'char':    $col['precision'] = 1; break;
 468                              case 'varchar': $col['precision'] = 255; break;
 469                          }
 470                      }
 471                      while (list($prop,$val) = each($col))
 472                      {
 473                          switch ($prop)
 474                          {
 475                              case 'default':
 476                              case 'type':    // selectbox ensures type is not empty
 477                              case 'precision':
 478                              case 'scale':
 479  //                            case 'nullable':
 480                                  if ($val != '' || $prop == 'nullable')
 481                                  {
 482                                      $table['fd'][$name][$prop] = $prop=='default'&& $val=="''" ? '' : $val;
 483                                  }
 484                                  break;
 485                              case 'notnull':
 486                                  if ($val)
 487                                  {
 488                                      $table['fd'][$name]['nullable'] = False;
 489                                  }
 490                                  break;
 491                              case 'pk':
 492                              case 'uc':
 493                              case 'ix':
 494                                  if ($val)
 495                                  {
 496                                      if ($col['options'])
 497                                      {
 498                                          $opts = array();
 499                                          foreach(explode(',',$col['options']) as $opt)
 500                                          {
 501                                              list($db,$opt) = split('[(:)]',$opt);
 502                                              $opts[$db] = is_numeric($opt) ? intval($opt) : $opt;
 503                                          }
 504                                          $table[$prop][] = array(
 505                                              $name,
 506                                              'options' => $opts
 507                                          );
 508                                      }
 509                                      else
 510                                      {
 511                                          $table[$prop][] = $name;
 512                                      }
 513                                  }
 514                                  break;
 515                              case 'fk':
 516                                  if ($val != '')
 517                                  {
 518                                      $table['fk'][$name] = $val;
 519                                  }
 520                                  break;
 521                          }
 522                      }
 523                      $num2col[$n] = $col['name'];
 524                  }
 525              }
 526              foreach($content['Index'] as $n => $index)
 527              {
 528                  $idx_arr = array();
 529                  foreach($index as $key => $num)
 530                  {
 531                      if (is_numeric($key) && $num && @$num2col[$num])
 532                      {
 533                          $idx_arr[] = $num2col[$num];
 534                      }
 535                  }
 536                  if (count($idx_arr) && !isset($content['delete_index'][$n]))
 537                  {
 538                      if ($index['unique'])
 539                      {
 540                          $table['uc'][] = $idx_arr;
 541                      }
 542                      else
 543                      {
 544                          $table['ix'][] = $idx_arr;
 545                      }
 546                  }
 547              }
 548              if ($this->debug >= 2)
 549              {
 550                  echo "<p>content2table: table ="; _debug_array($table);
 551                  echo "<p>changes = "; _debug_array($this->changes);
 552              }
 553              return $table;
 554          }
 555  
 556          /**
 557           * includes $app/setup/tables_current.inc.php
 558           * @param string $app application name
 559           * @param array &$phpgw_baseline where to return the data
 560           * @return boolean True if file found, False else
 561           */
 562  		function read($app,&$phpgw_baseline)
 563          {
 564              $file = EGW_SERVER_ROOT."/$app/setup/tables_current.inc.php";
 565  
 566              $phpgw_baseline = array();
 567  
 568              if ($app != '' && file_exists($file))
 569              {
 570                  include($file);
 571              }
 572              else
 573              {
 574                  return False;
 575              }
 576              if ($this->debug >= 5)
 577              {
 578                  echo "<p>read($app): file='$file', phpgw_baseline =";
 579                  _debug_array($phpgw_baseline);
 580              }
 581              return True;
 582          }
 583  
 584          /**
 585           * returns an array as string in php-notation
 586           *
 587           * @param array $arr
 588           * @param int $depth for idention
 589           * @param string $parent
 590           * @return string
 591           */
 592  		function write_array($arr,$depth,$parent='')
 593          {
 594              if (in_array($parent,array('pk','fk','ix','uc')))
 595              {
 596                  $depth = 0;
 597              }
 598              if ($depth)
 599              {
 600                  $tabs = "\n";
 601                  for ($n = 0; $n < $depth; ++$n)
 602                  {
 603                      $tabs .= "\t";
 604                  }
 605                  ++$depth;
 606              }
 607              $def = "array($tabs".($tabs ? "\t" : '');
 608  
 609              $n = 0;
 610              foreach($arr as $key => $val)
 611              {
 612                  if (!is_int($key))
 613                  {
 614                      $def .= "'$key' => ";
 615                  }
 616                  if (is_array($val))
 617                  {
 618                      $def .= $this->write_array($val,$parent == 'fd' ? 0 : $depth,$key);
 619                  }
 620                  else
 621                  {
 622                      if (!$only_vals && $key === 'nullable')
 623                      {
 624                          $def .= $val ? 'True' : 'False';
 625                      }
 626                      else
 627                      {
 628                          $def .= "'$val'";
 629                      }
 630                  }
 631                  if ($n < count($arr)-1)
 632                  {
 633                      $def .= ",$tabs".($tabs ? "\t" : '');
 634                  }
 635                  ++$n;
 636              }
 637              $def .= "$tabs)";
 638  
 639              return $def;
 640          }
 641  
 642          /**
 643           * writes tabledefinitions $phpgw_baseline to file /$app/setup/tables_current.inc.php
 644           *
 645           * @param string $app app-name
 646           * @param array $phpgw_baseline tabledefinitions
 647           * @return boolean True if file writen else False
 648           */
 649  		function write($app,$phpgw_baseline)
 650          {
 651              $file = EGW_SERVER_ROOT."/$app/setup/tables_current.inc.php";
 652  
 653              if (file_exists($file) && ($f = fopen($file,'r')))
 654              {
 655                  $header = fread($f,filesize($file));
 656                  if ($end = strpos($header,');'))
 657                  {
 658                      $footer = substr($header,$end+3);    // this preservs other stuff, which should not be there
 659                  }
 660                  $header = substr($header,0,strpos($header,'$phpgw_baseline'));
 661                  fclose($f);
 662  
 663                  if (is_writable(EGW_SERVER_ROOT."/$app/setup"))
 664                  {
 665                      $old_file = EGW_SERVER_ROOT . "/$app/setup/tables_current.old.inc.php";
 666                      if (file_exists($old_file))
 667                      {
 668                          unlink($old_file);
 669                      }
 670                      rename($file,$old_file);
 671                  }
 672                  while ($header[strlen($header)-1] == "\t")
 673                  {
 674                      $header = substr($header,0,strlen($header)-1);
 675                  }
 676              }
 677              if (!$header)
 678              {
 679                  $header = $this->setup_header . "\n\n";
 680              }
 681              if (!is_writeable(EGW_SERVER_ROOT."/$app/setup") || !($f = fopen($file,'w')))
 682              {
 683                  return False;
 684              }
 685              $def .= "\t\$phpgw_baseline = ";
 686              $def .= $this->write_array($phpgw_baseline,1);
 687              $def .= ";\n";
 688  
 689              fwrite($f,$header . $def . $footer);
 690              fclose($f);
 691  
 692              return True;
 693          }
 694  
 695          /**
 696           * reads and updates the version and tables info in file $app/setup/setup.inc.php
 697           * @param string $app the app
 698           * @param string $new new version number to set, if $new != ''
 699           * @param string $tables new tables to include (comma delimited), if != ''
 700           * @return the version or False if the file could not be read or written
 701           */
 702  		function setup_version($app,$new = '',$tables='')
 703          {
 704              //echo "<p>etemplate.db_tools.setup_version('$app','$new','$tables')</p>\n";
 705  
 706              $file = EGW_SERVER_ROOT."/$app/setup/setup.inc.php";
 707              if (file_exists($file))
 708              {
 709                  include($file);
 710              }
 711              if (!is_array($setup_info[$app]) || !isset($setup_info[$app]['version']))
 712              {
 713                  return False;
 714              }
 715              if (($new == '' || $setup_info[$app]['version'] == $new) &&    
 716                      (!$tables || $setup_info[$app]['tables'] && "'".implode("','",$setup_info[$app]['tables'])."'" == $tables))
 717              {
 718                  return $setup_info[$app]['version'];    // no change requested or not necessary 
 719              }
 720              if ($new == '') 
 721              {
 722                  $new = $setup_info[$app]['version'];
 723              }
 724              if (!($f = fopen($file,'r')))
 725              {
 726                  return False;
 727              }
 728              $fcontent = fread($f,filesize($file));
 729              fclose ($f);
 730              
 731              $app_pattern = "'$app'";
 732              if (preg_match("/define\('([^']+)',$app_pattern\)/",$fcontent,$matches))
 733              {
 734                  $app_pattern = $matches[1];
 735              }
 736              if (is_writable(EGW_SERVER_ROOT."/$app/setup"))
 737              {
 738                  $old_file = EGW_SERVER_ROOT . "/$app/setup/setup.old.inc.php";
 739                  if (file_exists($old_file))
 740                  {
 741                      unlink($old_file);
 742                  }
 743                  rename($file,$old_file);
 744              }
 745              $fnew = preg_replace('/(.*\\$'."setup_info\\[$app_pattern\\]\\['version'\\][ \\t]*=[ \\t]*)'[^']*'(.*)/i","\\1'$new'\\2",$fcontent);
 746              
 747              if ($tables != '')
 748              {
 749                  if (isset($setup_info[$app]['tables']))    // if there is already tables array, update it
 750                  {
 751                      $fnew = preg_replace('/(.*\\$'."setup_info\\[$app_pattern\\]\\['tables'\\][ \\t]*=[ \\t]*array\()[^)]*/i","\\1$tables",$fwas=$fnew);
 752  
 753                      if ($fwas == $fnew)    // nothing changed => tables are in single lines
 754                      {
 755                          $fwas = explode("\n",$fwas);
 756                          $fnew = $prefix = '';
 757                          $stage = 0;    // 0 = before, 1 = in, 2 = after tables section
 758                          foreach($fwas as $line)
 759                          {
 760                              if (preg_match('/(.*\\$'."setup_info\\[$app_pattern\\]\\['tables'\\]\\[[ \\t]*\\][ \\t]*=[ \\t]*)'/i",$line,$parts))
 761                              {
 762                                  if ($stage == 0)    // first line of tables-section
 763                                  {
 764                                      $stage = 1;
 765                                      $prefix = $parts[1];
 766                                  }
 767                              }
 768                              else                    // not in table-section
 769                              {
 770                                  if ($stage == 1)    // first line after tables-section ==> add it
 771                                  {
 772                                      $tables = explode(',',$tables);
 773                                      foreach ($tables as $table)
 774                                      {
 775                                          $fnew .= $prefix . $table . ";\n";
 776                                      }
 777                                      $stage = 2; 
 778                                  }
 779                                  if (strpos($line,'?>') === False)    // dont write the closeing tag
 780                                  {
 781                                      $fnew .= $line . "\n";
 782                                  }
 783                              }
 784                          }
 785                      }
 786                  }
 787                  else    // add the tables array
 788                  {
 789                      if (strstr($fnew,'?>'))    // remove a closeing tag
 790                      {
 791                          $fnew = str_replace('?>','',$fnew);
 792                      }
 793                      $fnew .= "\t\$setup_info[$app_pattern]['tables'] = array($tables);\n";
 794                  }
 795              }
 796              if (!is_writeable(EGW_SERVER_ROOT."/$app/setup") || !($f = fopen($file,'w')))
 797              {
 798                  return False;
 799              }
 800              fwrite($f,$fnew);
 801              fclose($f);
 802  
 803              return $new;
 804          }
 805  
 806          /**
 807           * updates file /$app/setup/tables_update.inc.php to reflect changes in $current
 808           *
 809           * @param string $app app-name
 810           * @param array $current new tabledefinitions
 811           * @param string $version new version
 812           * @return boolean True if file writen else False
 813           */
 814  		function update($app,$current,$version)
 815          {
 816              //echo "<p>etemplate.db_tools.update('$app',...,'$version')</p>\n";
 817              if (!is_writable(EGW_SERVER_ROOT."/$app/setup"))
 818              {
 819                  return False;
 820              }
 821              $file_baseline = EGW_SERVER_ROOT."/$app/setup/tables_baseline.inc.php";
 822              $file_current  = EGW_SERVER_ROOT."/$app/setup/tables_current.inc.php";
 823              $file_update   = EGW_SERVER_ROOT."/$app/setup/tables_update.inc.php";
 824  
 825              if (!file_exists($file_baseline) && !copy($file_current,$file_baseline))
 826              {
 827                  //echo "<p>Can't copy $file_current to $file_baseline !!!</p>\n";
 828                  return False;
 829              }
 830              $old_version = $this->setup_version($app);
 831              $old_version_ = str_replace('.','_',$old_version);
 832  
 833              if (file_exists($file_update))
 834              {
 835                  $f = fopen($file_update,'r');
 836                  $update = fread($f,filesize($file_update));
 837                  $update = str_replace('?>','',$update);
 838                  fclose($f);
 839                  $old_file = EGW_SERVER_ROOT . "/$app/setup/tables_update.old.inc.php";
 840                  if (file_exists($old_file))
 841                  {
 842                      unlink($old_file);
 843                  }
 844                  rename($file_update,$old_file);
 845              }
 846              else
 847              {
 848                  $update = $this->setup_header;
 849              }
 850              $update .= "
 851      \$test[] = '$old_version';
 852      function $app"."_upgrade$old_version_()
 853      {\n";
 854  
 855              $update .= $this->update_schema($app,$current,$tables);
 856  
 857              $update .= "
 858          return \$GLOBALS['setup_info']['$app']['currentver'] = '$version';
 859      }
 860  ?".">\n";
 861              if (!($f = fopen($file_update,'w')))
 862              {
 863                  //echo "<p>Cant open '$update' for writing !!!</p>\n";
 864                  return False;
 865              }
 866              fwrite($f,$update);
 867              fclose($f);
 868  
 869              $this->setup_version($app,$version,$tables);
 870  
 871              return True;
 872          }
 873  
 874          /**
 875           * unsets all keys in an array which have a given value
 876           *
 877           * @param array &$arr
 878           * @param mixed $val value to check against
 879           */
 880  		function remove_from_array(&$arr,$value)
 881          {
 882              foreach($arr as $key => $val)
 883              {
 884                  if ($val == $value)
 885                  {
 886                      unset($arr[$key]);
 887                  }
 888              }
 889          }
 890  
 891          /**
 892           * creates an update-script 
 893           *
 894           * @param string $app app-name
 895           * @param array $current new table-defintion
 896           * @param string &$tables returns comma delimited list of new table-names
 897           * @return string the update-script
 898           */
 899  		function update_schema($app,$current,&$tables)
 900          {
 901              $this->read($app,$old);
 902  
 903              $tables = '';
 904              foreach($old as $name => $table_def)
 905              {
 906                  if (!isset($current[$name]))    // table $name droped
 907                  {
 908                      $update .= "\t\t\$GLOBALS['egw_setup']->oProc->DropTable('$name');\n";
 909                  }
 910                  else
 911                  {
 912                      $tables .= ($tables ? ',' : '') . "'$name'";
 913  
 914                      $new_table_def = $table_def;
 915                      foreach($table_def['fd'] as $col => $col_def)
 916                      {
 917                          if (!isset($current[$name]['fd'][$col]))    // column $col droped
 918                          {
 919                              if (!isset($this->changes[$name][$col]) || $this->changes[$name][$col] == '**deleted**')
 920                              {
 921                                  unset($new_table_def['fd'][$col]);
 922                                  $this->remove_from_array($new_table_def['pk'],$col);
 923                                  $this->remove_from_array($new_table_def['fk'],$col);
 924                                  $this->remove_from_array($new_table_def['ix'],$col);
 925                                  $this->remove_from_array($new_table_def['uc'],$col);
 926                                  $update .= "\t\t\$GLOBALS['egw_setup']->oProc->DropColumn('$name',";
 927                                  $update .= $this->write_array($new_table_def,2).",'$col');\n";
 928                              }
 929                              else    // column $col renamed
 930                              {
 931                                  $new_col = $this->changes[$name][$col];
 932                                  $update .= "\t\t\$GLOBALS['egw_setup']->oProc->RenameColumn('$name','$col','$new_col');\n";
 933                              }
 934                          }
 935                      }
 936                      if (is_array($this->changes[$name]))
 937                      {
 938                          foreach($this->changes[$name] as $col => $new_col)
 939                          {
 940                              if ($new_col != '**deleted**')
 941                              {
 942                                  $old[$name]['fd'][$new_col] = $old[$name]['fd'][$col];    // to be able to detect further changes of the definition
 943                                  unset($old[$name]['fd'][$col]);
 944                              }
 945                          }
 946                      }
 947                  }
 948              }
 949              foreach($current as $name => $table_def)
 950              {
 951                  if (!isset($old[$name]))    // table $name added
 952                  {
 953                      $tables .= ($tables ? ',' : '') . "'$name'";
 954  
 955                      $update .= "\t\t\$GLOBALS['egw_setup']->oProc->CreateTable('$name',";
 956                      $update .= $this->write_array($table_def,2).");\n";
 957                  }
 958                  else
 959                  {
 960                      $old_norm = $this->normalize($old[$name]);
 961                      $new_norm = $this->normalize($table_def);
 962                      $old_norm_fd = $old_norm['fd']; unset($old_norm['fd']);
 963                      $new_norm_fd = $new_norm['fd']; unset($new_norm['fd']);
 964  
 965                      // check if the indices are changed and refresh the table if so
 966                      $do_refresh = serialize($old_norm) != serialize($new_norm);
 967                      // we comment out the Add or AlterColumn code as it is not needed, but might be useful for more complex updates
 968                      foreach($table_def['fd'] as $col => $col_def)
 969                      {
 970                          if (($add = !isset($old[$name]['fd'][$col])) ||    // column $col added
 971                               serialize($old_norm_fd[$col]) != serialize($new_norm_fd[$col])) // column definition altered
 972                          {
 973                              $update .= "\t\t".($do_refresh ? "/* done by RefreshTable() anyway\n\t\t" : '').
 974                                  "\$GLOBALS['egw_setup']->oProc->".($add ? 'Add' : 'Alter')."Column('$name','$col',";
 975                              $update .= $this->write_array($col_def,2) . ');' . ($do_refresh ? '*/' : '') . "\n";
 976                          }
 977                      }
 978                      if ($do_refresh)
 979                      {
 980                          $update .= "\t\t\$GLOBALS['egw_setup']->oProc->RefreshTable('$name',";
 981                          $update .= $this->write_array($table_def,2).");\n";
 982                      }
 983                  }
 984              }
 985              if ($this->debug)
 986              {
 987                  echo "<p>update_schema($app, ...) =<br><pre>$update</pre>)</p>\n";
 988              }
 989              return $update;
 990          }
 991  
 992          /**
 993           * orders the single-colum-indices after the columns and the multicolunm ones behind
 994           * 
 995           * @param array $index array with indices
 996           * @param array $cols array with column-defs (col-name is the key)
 997           * @return array the new array of indices
 998           */
 999  		function normalize_index($index,$cols)
1000          {
1001              $normalized = array();
1002              foreach($cols as $col => $data)
1003              {
1004                  foreach($index as $n => $idx)
1005                  {
1006                      if ($idx == $col || is_array($idx) && $idx[0] == $col && !isset($idx[1]))
1007                      {
1008                          $normalized[] = isset($idx['options']) ? $idx : $col;
1009                          unset($index[$n]);
1010                          break;
1011                      }
1012                  }
1013              }
1014              foreach($index as $idx)
1015              {
1016                  $normalized[] = $idx;
1017              }
1018              return $normalized;
1019          }
1020  
1021          /**
1022           * normalices all properties in a table-definiton, eg. all nullable properties to True or False
1023           *
1024           * this is necessary to compare two table-defitions
1025           *
1026           * @param array $table table-definition
1027           * @return array the normaliced defintion
1028           */
1029  		function normalize($table)
1030          {
1031              $all_props = array('type','precision','nullable','default');
1032  
1033              foreach($table['fd'] as $col => $props)
1034              {
1035                  $table['fd'][$col] = array(
1036                      'type' => ''.$props['type'],
1037                      'precision' => 0+$props['precision'],
1038                      'scale' => 0+$props['scale'],
1039                      'nullable' => !isset($props['nullable']) || !!$props['nullable'],
1040                      'default' => ''.$props['default']
1041                  );
1042              }
1043              return array(
1044                  'fd' => $table['fd'],
1045                  'pk' => $table['pk'],
1046                  'fk' => $table['fk'],
1047                  'ix' => $this->normalize_index($table['ix'],$table['fd']),
1048                  'uc' => $this->normalize_index($table['uc'],$table['fd'])
1049              );
1050          }
1051  
1052          /**
1053           * compares two table-definitions, by comparing normaliced string-representations (serialize)
1054           *
1055           * @param array $a
1056           * @param array $b
1057           * @return boolean true if they are identical (would create an identical schema), false otherwise
1058           * 
1059           */
1060  		function tables_identical($a,$b)
1061          {
1062              $a = serialize($this->normalize($a));
1063              $b = serialize($this->normalize($b));
1064  
1065              //echo "<p>checking if tables identical = ".($a == $b ? 'True' : 'False')."<br>\n";
1066              //echo "a: $a<br>\nb: $b</p>\n";
1067  
1068              return $a == $b;
1069          }
1070      }


Généré le : Sun Feb 25 17:20:01 2007 par Balluche grâce à PHPXref 0.7