From d4477d501d3ffc9e08929fb5aef906dfd3e4c075 Mon Sep 17 00:00:00 2001
From: Ralf Becker
Date: Sun, 8 Aug 2004 09:19:06 +0000
Subject: [PATCH] fix for mbstring.func_overload 7: ereg_* where looping
infinit, changed to preg_*
---
etemplate/inc/class.db_tools.inc.php | 1045 ++++++++++++++++++++++++++
1 file changed, 1045 insertions(+)
create mode 100644 etemplate/inc/class.db_tools.inc.php
diff --git a/etemplate/inc/class.db_tools.inc.php b/etemplate/inc/class.db_tools.inc.php
new file mode 100644
index 0000000000..a763c77d54
--- /dev/null
+++ b/etemplate/inc/class.db_tools.inc.php
@@ -0,0 +1,1045 @@
+ *
+ * -------------------------------------------- *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms of the GNU General Public License as published by the *
+ * Free Software Foundation; either version 2 of the License, or (at your *
+ * option) any later version. *
+ \**************************************************************************/
+
+ /* $Id$ */
+
+ class db_tools
+ {
+ var $public_functions = array
+ (
+ 'edit' => True,
+ 'needs_save' => True,
+ //'admin' => True,
+ //'preferences' => True
+ );
+
+ var $debug = 0;
+ var $editor; // editor eTemplate
+ var $data; // Table definitions
+ var $app; // used app
+ var $table; // used table
+ var $types = array(
+ 'varchar' => 'varchar',
+ 'int' => 'int',
+ 'auto' => 'auto',
+ 'blob' => 'blob',
+ 'char' => 'char',
+ 'date' => 'date',
+ 'decimal' => 'decimal',
+ 'float' => 'float',
+ 'longtext' => 'longtext',
+ 'text' => 'text',
+ 'timestamp' => 'timestamp',
+// 'abstime' => 'abstime (mysql:timestamp)',
+ );
+ var $setup_header = 'editor = CreateObject('etemplate.etemplate','etemplate.db-tools.edit');
+ $this->data = array();
+
+ if (!is_array($GLOBALS['phpgw_info']['apps']) || !count($GLOBALS['phpgw_info']['apps']))
+ {
+ ExecMethod('phpgwapi.applications.read_installed_apps');
+ }
+ $GLOBALS['phpgw_info']['flags']['app_header'] =
+ $GLOBALS['phpgw_info']['apps']['etemplate']['title'].' - '.lang('DB-Tools');
+ }
+
+ /*!
+ @function edit
+ @syntax edit( $content='',$msg='' )
+ @author ralfbecker
+ @abstract this is the table editor (and the callback/submit-method too)
+ */
+ function edit($content='',$msg = '')
+ {
+ if (isset($_GET['app']))
+ {
+ $this->app = $_GET['app'];
+ }
+ if (is_array($content))
+ {
+ if ($this->debug)
+ {
+ echo "content ="; _debug_array($content);
+ }
+ $this->app = $content['app']; // this is what the user selected
+ $this->table = $content['table_name'];
+ $posted_app = $content['posted_app']; // this is the old selection
+ $posted_table = $content['posted_table'];
+ }
+ if ($posted_app && $posted_table && // user changed app or table
+ ($posted_app != $this->app || $posted_table != $this->table))
+ {
+ if ($this->needs_save('',$posted_app,$posted_table,$this->content2table($content)))
+ {
+ return;
+ }
+ $this->renames = array();
+ }
+ if (!$this->app)
+ {
+ $this->table = '';
+ $table_names = array('' => lang('none'));
+ }
+ else
+ {
+ $this->read($this->app,$this->data);
+
+ foreach($this->data as $name => $table)
+ {
+ $table_names[$name] = $name;
+ }
+ }
+ if (!$this->table || $this->app != $posted_app)
+ {
+ reset($this->data);
+ list($this->table) = each($this->data); // use first table
+ }
+ elseif ($this->app == $posted_app && $posted_table)
+ {
+ $this->data[$posted_table] = $this->content2table($content);
+ }
+ if ($content['write_tables'])
+ {
+ if ($this->needs_save('',$this->app,$this->table,$this->data[$posted_table]))
+ {
+ return;
+ }
+ $msg .= lang('Table unchanged, no write necessary !!!');
+ }
+ elseif ($content['delete'])
+ {
+ list($col) = each($content['delete']);
+
+ @reset($this->data[$posted_table]['fd']);
+ while ($col-- > 0 && list($key,$data) = @each($this->data[$posted_table]['fd'])) ;
+
+ unset($this->data[$posted_table]['fd'][$key]);
+ $this->changes[$posted_table][$key] = '**deleted**';
+ }
+ elseif ($content['add_column'])
+ {
+ $this->data[$posted_table]['fd'][''] = array();
+ }
+ elseif ($content['add_table'] || $content['import'])
+ {
+ if (!$this->app)
+ {
+ $msg .= lang('Select an app first !!!');
+ }
+ elseif (!$content['new_table_name'])
+ {
+ $msg .= lang('Please enter table-name first !!!');
+ }
+ elseif ($content['add_table'])
+ {
+ $this->table = $content['new_table_name'];
+ $this->data[$this->table] = array('fd' => array(),'pk' =>array(),'ix' => array(),'uc' => array(),'fk' => array());
+ $msg .= lang('New table created');
+ }
+ else // import
+ {
+ $oProc = CreateObject('phpgwapi.schema_proc',$GLOBALS['phpgw_info']['server']['db_type']);
+ $oProc->m_odb = $GLOBALS['phpgw']->db;
+ $oProc->m_oTranslator->_GetColumns($oProc,$content['new_table_name'],$nul);
+
+ while (list($key,$tbldata) = each ($oProc->m_oTranslator->sCol))
+ {
+ $cols .= $tbldata;
+ }
+ eval('$cols = array('. $cols . ');');
+
+ $this->data[$this->table = $content['new_table_name']] = array(
+ 'fd' => $cols,
+ 'pk' => $oProc->m_oTranslator->pk,
+ 'fk' => $oProc->m_oTranslator->fk,
+ 'ix' => $oProc->m_oTranslator->ix,
+ 'uc' => $oProc->m_oTranslator->uc
+ );
+ }
+ }
+ elseif ($content['editor'])
+ {
+ ExecMethod('etemplate.editor.edit');
+ return;
+ }
+ $add_index = isset($content['add_index']);
+
+ // from here on, filling new content for eTemplate
+ $content = array(
+ 'msg' => $msg,
+ 'table_name' => $this->table,
+ 'app' => $this->app,
+ );
+ if (!isset($table_names[$this->table])) // table is not jet written
+ {
+ $table_names[$this->table] = $this->table;
+ }
+ $sel_options = array(
+ 'table_name' => $table_names,
+ 'type' => $this->types
+ );
+ if ($this->table != '' && isset($this->data[$this->table]))
+ {
+ $content += $this->table2content($this->data[$this->table],$sel_options['Index'],$add_index);
+ }
+ $no_button = array( );
+ if (!$this->app || !$this->table)
+ {
+ $no_button += array('write_tables' => True);
+ }
+ if ($this->debug)
+ {
+ echo 'editor.edit: content ='; _debug_array($content);
+ }
+ $this->editor->exec('etemplate.db_tools.edit',$content,$sel_options,$no_button,
+ array('posted_table' => $this->table,'posted_app' => $this->app,'changes' => $this->changes));
+ }
+
+ /*!
+ @function needs_save
+ @syntax needs_save( $cont='',$posted_app='',$posted_table='',$edited_table='' )
+ @author ralfbecker
+ @abstract checks if table was changed and if so offers user to save changes
+ @param $cont the content of the form (if called by process_exec)
+ @param $posted_app the app the table is from
+ @param $posted_table the table-name
+ @param $edited_table the edited table-definitions
+ @result only if no changes
+ */
+ function needs_save($cont='',$posted_app='',$posted_table='',$edited_table='')
+ {
+ if (!$posted_app && is_array($cont))
+ {
+ if (isset($cont['yes']))
+ {
+ $this->app = $cont['app'];
+ $this->table = $cont['table'];
+ $this->read($this->app,$this->data);
+ $this->data[$this->table] = $cont['edited_table'];
+ $this->changes = $cont['changes'];
+ if ($cont['new_version'])
+ {
+ $this->update($this->app,$this->data,$cont['new_version']);
+ }
+ else
+ {
+ foreach($this->data as $tname => $tinfo)
+ {
+ $tables .= ($tables ? ',' : '') . "'$tname'";
+ }
+ $this->setup_version($this->app,'',$tables);
+ }
+ $msg .= $this->write($this->app,$this->data) ?
+ lang('File writen') : lang('Error: writing file (no write-permission for the webserver) !!!');
+ }
+ $this->changes = array();
+ // return to edit with everything set, so the user gets the table he asked for
+ $this->edit(array(
+ 'app' => $cont['new_app'],
+ 'table_name' => $cont['app']==$cont['new_app'] ? $cont['new_table'] : '',
+ 'posted_app' => $cont['new_app']
+ ),$msg);
+
+ return True;
+ }
+ $new_app = $this->app; // these are the ones, the users whiches to change too
+ $new_table = $this->table;
+
+ $this->app = $posted_app;
+ $this->data = array();
+ $this->read($posted_app,$this->data);
+
+ if (isset($this->data[$posted_table]) &&
+ $this->tables_identical($this->data[$posted_table],$edited_table))
+ {
+ if ($new_app != $this->app) // are we changeing the app, or hit the user just write
+ {
+ $this->app = $new_app; // if we change init the data empty
+ $this->data = array();
+ }
+ return False; // continue edit
+ }
+ $content = array(
+ 'app' => $posted_app,
+ 'table' => $posted_table,
+ 'version' => $this->setup_version($posted_app)
+ );
+ $preserv = $content + array(
+ 'new_app' => $new_app,
+ 'new_table' => $new_table,
+ 'edited_table' => $edited_table,
+ 'changes' => $this->changes
+ );
+ $new_version = explode('.',$content['version']);
+ $minor = count($new_version)-1;
+ $new_version[$minor] = sprintf('%03d',1+$new_version[$minor]);
+ $content['new_version'] = implode('.',$new_version);
+
+ $tmpl = new etemplate('etemplate.db-tools.ask_save');
+
+ if (!file_exists(PHPGW_SERVER_ROOT."/$posted_app/setup/tables_current.inc.php"))
+ {
+ $tmpl->disable_cells('version');
+ $tmpl->disable_cells('new_version');
+ }
+ $tmpl->exec('etemplate.db_tools.needs_save',$content,array(),array(),$preserv);
+
+ return True; // dont continue in edit
+ }
+
+ /*!
+ @function has_single_index
+ @syntax has_single_index( $col,$index )
+ @author ralfbecker
+ @abstract checks if there is an index (only) on $col (not a multiple index incl. $col)
+ @param $col column name
+ @param $index ix or uc array of table-defintion
+ @result True if $col has a single index
+ */
+ function has_single_index($col,$index,&$options)
+ {
+ foreach($index as $in)
+ {
+ if ($in == $col || is_array($in) && $in[0] == $col && !isset($in[1]))
+ {
+ if ($in != $col && isset($in['options']))
+ {
+ foreach($in['options'] as $db => $opts)
+ {
+ $options[] = $db.'('.(is_array($opts)?implode(',',$opts):$opts).')';
+ }
+ $options = implode(', ',$options);
+ }
+ return True;
+ }
+ }
+ return False;
+ }
+
+ /*!
+ @function table2content
+ @syntax table2content( $table )
+ @author ralfbecker
+ @abstract creates content-array from a $table
+ @param $table table-definition, eg. $phpgw_baseline[$table_name]
+ @result content-array
+ */
+ function table2content($table,&$columns,$extra_index=False)
+ {
+ $content = $columns = array();
+ for ($n = 1; list($col_name,$col_defs) = each($table['fd']); ++$n)
+ {
+ $col_defs['name'] = $col_name;
+ $col_defs['pk'] = in_array($col_name,$table['pk']);
+ $col_defs['uc'] = $this->has_single_index($col_name,$table['uc'],$col_defs['options']);
+ $col_defs['ix'] = $this->has_single_index($col_name,$table['ix'],$col_defs['options']);
+ $col_defs['fk'] = $table['fk'][$col_name];
+ if (isset($col_defs['default']) && $col_defs['default'] == '')
+ {
+ $col_defs['default'] = is_int($col_defs['default']) ? '0' : "''"; // spezial value for empty, but set, default
+ }
+ $col_defs['notnull'] = isset($col_defs['nullable']) && !$col_defs['nullable'];
+
+ $col_defs['n'] = $n;
+
+ $content["Row$n"] = $col_defs;
+
+ $columns[$n] = $col_name;
+ }
+ $n = 2;
+ foreach(array('uc','ix') as $type)
+ {
+ foreach($table[$type] as $index)
+ {
+ if (is_array($index) && isset($index[1])) // multicolum index
+ {
+ $content['Index'][$n]['unique'] = $type == 'uc';
+ $content['Index'][$n]['n'] = $n - 1;
+ foreach($index as $col)
+ {
+ $content['Index'][$n][] = array_search($col,$columns);
+ }
+ ++$n;
+ }
+ }
+ }
+ if ($extra_index)
+ {
+ $content['Index'][$n]['n'] = $n-1;
+ }
+ if ($this->debug >= 3)
+ {
+ echo "table2content(,,'$extra_index'): content ="; _debug_array($content);
+ echo "
columns ="; _debug_array($columns);
+ }
+ return $content;
+ }
+
+ /*!
+ @function content2table
+ @syntax content2table( $content )
+ @author ralfbecker
+ @abstract creates table-definition from posted content
+ @param $content posted content-array
+ @note It sets some reasonalbe defaults for not set precisions (else setup will not install)
+ @result table-definition
+ */
+ function content2table($content)
+ {
+ if (!is_array($this->data))
+ {
+ $this->read($content['posted_app'],$this->data);
+ }
+ $old_cols = $this->data[$posted_table = $content['posted_table']]['fd'];
+ $this->changes = $content['changes'];
+
+ $table = array();
+ $table['fd'] = array(); // do it in the default order of tables_*
+ $table['pk'] = array();
+ $table['fk'] = array();
+ $table['ix'] = array();
+ $table['uc'] = array();
+ for (reset($content),$n = 1; isset($content["Row$n"]); ++$n)
+ {
+ $col = $content["Row$n"];
+
+ while ((list($old_name,$old_col) = @each($old_cols)) &&
+ $this->changes[$posted_table][$old_name] == '**deleted**') ;
+
+ if (($name = $col['name']) != '') // ignoring lines without column-name
+ {
+ if ($col['name'] != $old_name && $n <= count($old_cols)) // column renamed --> remeber it
+ {
+ $this->changes[$posted_table][$old_name] = $col['name'];
+ //echo "
content2table: $posted_table.$old_name renamed to $col[name]
\n";
+ }
+ if ($col['precision'] <= 0)
+ {
+ switch ($col['type']) // set some defaults for precision, else setup fails
+ {
+ case 'float':
+ case 'int': $col['precision'] = 4; break;
+ case 'char': $col['precision'] = 1; break;
+ case 'varchar': $col['precision'] = 255; break;
+ }
+ }
+ while (list($prop,$val) = each($col))
+ {
+ switch ($prop)
+ {
+ case 'default':
+ case 'type': // selectbox ensures type is not empty
+ case 'precision':
+ case 'scale':
+// case 'nullable':
+ if ($val != '' || $prop == 'nullable')
+ {
+ $table['fd'][$name][$prop] = $prop=='default'&& $val=="''" ? '' : $val;
+ }
+ break;
+ case 'notnull':
+ if ($val)
+ {
+ $table['fd'][$name]['nullable'] = False;
+ }
+ break;
+ case 'pk':
+ case 'uc':
+ case 'ix':
+ if ($val)
+ {
+ if ($col['options'])
+ {
+ $opts = array();
+ foreach(explode(',',$col['options']) as $opt)
+ {
+ list($db,$opt) = split('[(:)]',$opt);
+ $opts[$db] = is_numeric($opt) ? intval($opt) : $opt;
+ }
+ $table[$prop][] = array(
+ $name,
+ 'options' => $opts
+ );
+ }
+ else
+ {
+ $table[$prop][] = $name;
+ }
+ }
+ break;
+ case 'fk':
+ if ($val != '')
+ {
+ $table['fk'][$name] = $val;
+ }
+ break;
+ }
+ }
+ $num2col[$n] = $col['name'];
+ }
+ }
+ foreach($content['Index'] as $n => $index)
+ {
+ $idx_arr = array();
+ foreach($index as $key => $num)
+ {
+ if (is_numeric($key) && $num && @$num2col[$num])
+ {
+ $idx_arr[] = $num2col[$num];
+ }
+ }
+ if (count($idx_arr) && !isset($content['delete_index'][$n]))
+ {
+ if ($index['unique'])
+ {
+ $table['uc'][] = $idx_arr;
+ }
+ else
+ {
+ $table['ix'][] = $idx_arr;
+ }
+ }
+ }
+ if ($this->debug >= 2)
+ {
+ echo "content2table: table ="; _debug_array($table);
+ echo "
changes = "; _debug_array($this->changes);
+ }
+ return $table;
+ }
+
+ /*!
+ @function read
+ @syntax read( $app,&$phpgw_baseline )
+ @author ralfbecker
+ @abstract includes $app/setup/tables_current.inc.php
+ @param $app application name
+ @param $phpgw_baseline where to put the data
+ @result True if file found, False else
+ */
+ function read($app,&$phpgw_baseline)
+ {
+ $file = PHPGW_SERVER_ROOT."/$app/setup/tables_current.inc.php";
+
+ $phpgw_baseline = array();
+
+ if ($app != '' && file_exists($file))
+ {
+ include($file);
+ }
+ else
+ {
+ return False;
+ }
+ if ($this->debug >= 5)
+ {
+ echo "
read($app): file='$file', phpgw_baseline =";
+ _debug_array($phpgw_baseline);
+ }
+ return True;
+ }
+
+ function write_array($arr,$depth,$parent='')
+ {
+ if (in_array($parent,array('pk','fk','ix','uc')))
+ {
+ $depth = 0;
+ }
+ if ($depth)
+ {
+ $tabs = "\n";
+ for ($n = 0; $n < $depth; ++$n)
+ {
+ $tabs .= "\t";
+ }
+ ++$depth;
+ }
+ $def = "array($tabs".($tabs ? "\t" : '');
+
+ $n = 0;
+ foreach($arr as $key => $val)
+ {
+ if (!is_int($key))
+ {
+ $def .= "'$key' => ";
+ }
+ if (is_array($val))
+ {
+ $def .= $this->write_array($val,$parent == 'fd' ? 0 : $depth,$key);
+ }
+ else
+ {
+ if (!$only_vals && $key === 'nullable')
+ {
+ $def .= $val ? 'True' : 'False';
+ }
+ else
+ {
+ $def .= "'$val'";
+ }
+ }
+ if ($n < count($arr)-1)
+ {
+ $def .= ",$tabs".($tabs ? "\t" : '');
+ }
+ ++$n;
+ }
+ $def .= "$tabs)";
+
+ return $def;
+ }
+
+ /*!
+ @function write
+ @syntax write( $app,$phpgw_baseline )
+ @author ralfbecker
+ @abstract writes tabledefinitions $phpgw_baseline to file /$app/setup/tables_current.inc.php
+ @param $app app-name
+ @param $phpgw_baseline tabledefinitions
+ @return True if file writen else False
+ */
+ function write($app,$phpgw_baseline)
+ {
+ $file = PHPGW_SERVER_ROOT."/$app/setup/tables_current.inc.php";
+
+ if (file_exists($file) && ($f = fopen($file,'r')))
+ {
+ $header = fread($f,filesize($file));
+ if ($end = strpos($header,');'))
+ {
+ $footer = substr($header,$end+3); // this preservs other stuff, which should not be there
+ }
+ $header = substr($header,0,strpos($header,'$phpgw_baseline'));
+ fclose($f);
+
+ if (is_writable(PHPGW_SERVER_ROOT."/$app/setup"))
+ {
+ $old_file = PHPGW_SERVER_ROOT . "/$app/setup/tables_current.old.inc.php";
+ if (file_exists($old_file))
+ {
+ unlink($old_file);
+ }
+ rename($file,$old_file);
+ }
+ while ($header[strlen($header)-1] == "\t")
+ {
+ $header = substr($header,0,strlen($header)-1);
+ }
+ }
+ if (!$header)
+ {
+ $header = $this->setup_header . "\n\n";
+ }
+ if (!is_writeable(PHPGW_SERVER_ROOT."/$app/setup") || !($f = fopen($file,'w')))
+ {
+ return False;
+ }
+ $def .= "\t\$phpgw_baseline = ";
+ $def .= $this->write_array($phpgw_baseline,1);
+ $def .= ";\n";
+
+ fwrite($f,$header . $def . $footer);
+ fclose($f);
+
+ return True;
+ }
+
+ /*!
+ @function setup_version
+ @syntax setup_version( $app,$new = '',$tables='' )
+ @author ralfbecker
+ @abstract reads and updates the version and tables info in file $app/setup/setup.inc.php
+ @param $app the app
+ @param $new new version number to set, if $new != ''
+ @param $tables new tables to include, if $tables != ''
+ @return the version or False if the file could not be read or written
+ */
+ function setup_version($app,$new = '',$tables='')
+ {
+ //echo "
etemplate.db_tools.setup_version('$app','$new','$tables')
\n";
+
+ $file = PHPGW_SERVER_ROOT."/$app/setup/setup.inc.php";
+ if (file_exists($file))
+ {
+ include($file);
+ }
+ if (!is_array($setup_info[$app]) || !isset($setup_info[$app]['version']))
+ {
+ return False;
+ }
+ if (($new == '' || $setup_info[$app]['version'] == $new) &&
+ (!$tables || $setup_info[$app]['tables'] && "'".implode("','",$setup_info[$app]['tables'])."'" == $tables))
+ {
+ return $setup_info[$app]['version']; // no change requested or not necessary
+ }
+ if ($new == '')
+ {
+ $new = $setup_info[$app]['version'];
+ }
+ if (!($f = fopen($file,'r')))
+ {
+ return False;
+ }
+ $fcontent = fread($f,filesize($file));
+ fclose ($f);
+
+ if (is_writable(PHPGW_SERVER_ROOT."/$app/setup"))
+ {
+ $old_file = PHPGW_SERVER_ROOT . "/$app/setup/setup.old.inc.php";
+ if (file_exists($old_file))
+ {
+ unlink($old_file);
+ }
+ rename($file,$old_file);
+ }
+ $fnew = preg_replace('/(.*\\$'."setup_info\\['$app'\\]\\['version'\\][ \\t]*=[ \\t]*)'[^']*'(.*)/i","\\1'$new'\\2",$fcontent);
+
+ if ($tables != '')
+ {
+ if ($setup_info[$app]['tables']) // if there is already tables array, update it
+ {
+ $fnew = preg_replace('/(.*\\$'."setup_info\\['$app'\\]\\['tables'\\][ \\t]*=[ \\t]*array\()[^)]*/i","\\1$tables",$fwas=$fnew);
+
+ if ($fwas == $fnew) // nothing changed => tables are in single lines
+ {
+ $fwas = explode("\n",$fwas);
+ $fnew = $prefix = '';
+ $stage = 0; // 0 = before, 1 = in, 2 = after tables section
+ foreach($fwas as $line)
+ {
+ if (preg_match('/(.*\\$'."setup_info\\['$app'\\]\\['tables'\\]\\[[ \\t]*\\][ \\t]*=[ \\t]*)'/i",$line,$parts))
+ {
+ if ($stage == 0) // first line of tables-section
+ {
+ $stage = 1;
+ $prefix = $parts[1];
+ }
+ }
+ else // not in table-section
+ {
+ if ($stage == 1) // first line after tables-section ==> add it
+ {
+ $tables = explode(',',$tables);
+ foreach ($tables as $table)
+ {
+ $fnew .= $prefix . $table . ";\n";
+ }
+ $stage = 2;
+ }
+ if (strpos($line,'?>') === False) // dont write the closeing tag
+ {
+ $fnew .= $line . "\n";
+ }
+ }
+ }
+ }
+ }
+ else // add the tables array
+ {
+ if (strstr($fnew,'?>')) // remove a closeing tag
+ {
+ $fnew = str_replace('?>','',$fnew);
+ }
+ $fnew .= "\t\$setup_info['$app']['tables'] = array($tables);\n";
+ }
+ }
+ if (!is_writeable(PHPGW_SERVER_ROOT."/$app/setup") || !($f = fopen($file,'w')))
+ {
+ return False;
+ }
+ fwrite($f,$fnew);
+ fclose($f);
+
+ return $new;
+ }
+
+ /*!
+ @function update
+ @syntax update( $app,$current,$version )
+ @author ralfbecker
+ @abstract updates file /$app/setup/tables_update.inc.php to reflect changes in $current
+ @param $app app-name
+ @param $current new tabledefinitions
+ @param $version new version
+ @return True if file writen else False
+ */
+ function update($app,$current,$version)
+ {
+ //echo "etemplate.db_tools.update('$app',...,'$version')
\n";
+
+ if (!is_writable(PHPGW_SERVER_ROOT."/$app/setup"))
+ {
+ return False;
+ }
+ $file_baseline = PHPGW_SERVER_ROOT."/$app/setup/tables_baseline.inc.php";
+ $file_current = PHPGW_SERVER_ROOT."/$app/setup/tables_current.inc.php";
+ $file_update = PHPGW_SERVER_ROOT."/$app/setup/tables_update.inc.php";
+
+ if (!file_exists($file_baseline) && !copy($file_current,$file_baseline))
+ {
+ //echo "Can't copy $file_current to $file_baseline !!!
\n";
+ return False;
+ }
+ $old_version = $this->setup_version($app);
+ $old_version_ = str_replace('.','_',$old_version);
+
+ if (file_exists($file_update))
+ {
+ $f = fopen($file_update,'r');
+ $update = fread($f,filesize($file_update));
+ $update = str_replace('?>','',$update);
+ fclose($f);
+ $old_file = PHPGW_SERVER_ROOT . "/$app/setup/tables_update.old.inc.php";
+ if (file_exists($old_file))
+ {
+ unlink($old_file);
+ }
+ rename($file_update,$old_file);
+ }
+ else
+ {
+ $update = $this->setup_header;
+ }
+ $update .= "
+ \$test[] = '$old_version';
+ function $app"."_upgrade$old_version_()
+ {\n";
+
+ $update .= $this->update_schema($app,$current,$tables);
+
+ $update .= "
+ \$GLOBALS['setup_info']['$app']['currentver'] = '$version';
+ return \$GLOBALS['setup_info']['$app']['currentver'];
+ }
+?".">\n";
+ if (!($f = fopen($file_update,'w')))
+ {
+ //echo "Cant open '$update' for writing !!!
\n";
+ return False;
+ }
+ fwrite($f,$update);
+ fclose($f);
+
+ $this->setup_version($app,$version,$tables);
+
+ return True;
+ }
+
+ function remove_from_array(&$arr,$value)
+ {
+ foreach($arr as $key => $val)
+ {
+ if ($val == $value)
+ {
+ unset($arr[$key]);
+ }
+ }
+ }
+
+ function update_schema($app,$current,&$tables)
+ {
+ $this->read($app,$old);
+
+ $tables = '';
+ foreach($old as $name => $table_def)
+ {
+ if (!isset($current[$name])) // table $name droped
+ {
+ $update .= "\t\t\$GLOBALS['phpgw_setup']->oProc->DropTable('$name');\n";
+ }
+ else
+ {
+ $tables .= ($tables ? ',' : '') . "'$name'";
+
+ $new_table_def = $table_def;
+ foreach($table_def['fd'] as $col => $col_def)
+ {
+ if (!isset($current[$name]['fd'][$col])) // column $col droped
+ {
+ if (!isset($this->changes[$name][$col]) || $this->changes[$name][$col] == '**deleted**')
+ {
+ unset($new_table_def['fd'][$col]);
+ $this->remove_from_array($new_table_def['pk'],$col);
+ $this->remove_from_array($new_table_def['fk'],$col);
+ $this->remove_from_array($new_table_def['ix'],$col);
+ $this->remove_from_array($new_table_def['uc'],$col);
+ $update .= "\t\t\$GLOBALS['phpgw_setup']->oProc->DropColumn('$name',";
+ $update .= $this->write_array($new_table_def,2).",'$col');\n";
+ }
+ else // column $col renamed
+ {
+ $new_col = $this->changes[$name][$col];
+ $update .= "\t\t\$GLOBALS['phpgw_setup']->oProc->RenameColumn('$name','$col','$new_col');\n";
+ }
+ }
+ }
+ if (is_array($this->changes[$name]))
+ {
+ foreach($this->changes[$name] as $col => $new_col)
+ {
+ if ($new_col != '**deleted**')
+ {
+ $old[$name]['fd'][$new_col] = $old[$name]['fd'][$col]; // to be able to detect further changes of the definition
+ unset($old[$name]['fd'][$col]);
+ }
+ }
+ }
+ }
+ }
+ foreach($current as $name => $table_def)
+ {
+ if (!isset($old[$name])) // table $name added
+ {
+ $tables .= ($tables ? ',' : '') . "'$name'";
+
+ $update .= "\t\t\$GLOBALS['phpgw_setup']->oProc->CreateTable('$name',";
+ $update .= $this->write_array($table_def,2).");\n";
+ }
+ else
+ {
+ $old_norm = $this->normalize($old[$name]);
+ $new_norm = $this->normalize($table_def);
+ $old_norm_fd = $old_norm['fd']; unset($old_norm['fd']);
+ $new_norm_fd = $new_norm['fd']; unset($new_norm['fd']);
+
+ // check if the indices are changed and refresh the table if so
+ $do_refresh = serialize($old_norm) != serialize($new_norm);
+ // we comment out the Add or AlterColumn code as it is not needed, but might be useful for more complex updates
+ foreach($table_def['fd'] as $col => $col_def)
+ {
+ if (($add = !isset($old[$name]['fd'][$col])) || // column $col added
+ serialize($old_norm_fd[$col]) != serialize($new_norm_fd[$col])) // column definition altered
+ {
+ $update .= "\t\t".($do_refresh ? "/* done by RefreshTable() anyway\n\t\t" : '').
+ "\$GLOBALS['phpgw_setup']->oProc->".($add ? 'Add' : 'Alter')."Column('$name','$col',";
+ $update .= $this->write_array($col_def,2) . ');' . ($do_refresh ? '*/' : '') . "\n";
+ }
+ }
+ if ($do_refresh)
+ {
+ $update .= "\t\t\$GLOBALS['phpgw_setup']->oProc->RefreshTable('$name',";
+ $update .= $this->write_array($table_def,2).");\n";
+ }
+ }
+ }
+ if ($this->debug)
+ {
+ echo "update_schema($app, ...) =
$update
)
\n";
+ }
+ return $update;
+ }
+
+ /*!
+ @function normalize_index
+ @abstract orders the single-colum-indices after the columns and the multicolunm ones bedind
+ @syntax normalize_index( $index,$cols )
+ @param index array with indices
+ @param cols array with column-defs (col-name in key)
+ @author ralfbecker
+ @result the new array
+ */
+ function normalize_index($index,$cols)
+ {
+ $normalized = array();
+ foreach($cols as $col => $data)
+ {
+ foreach($index as $n => $idx)
+ {
+ if ($idx == $col || is_array($idx) && $idx[0] == $col && !isset($idx[1]))
+ {
+ $normalized[] = isset($idx['options']) ? $idx : $col;
+ unset($index[$n]);
+ break;
+ }
+ }
+ }
+ foreach($index as $idx)
+ {
+ $normalized[] = $idx;
+ }
+ return $normalized;
+ }
+
+ /*!
+ @function normalize
+ @syntax normalize( $table )
+ @author ralfbecker
+ @abstract sets all nullable properties to True or False
+ @result the new array
+ */
+ function normalize($table)
+ {
+ $all_props = array('type','precision','nullable','default');
+
+ foreach($table['fd'] as $col => $props)
+ {
+ $table['fd'][$col] = array(
+ 'type' => ''.$props['type'],
+ 'precision' => 0+$props['precision'],
+ 'scale' => 0+$props['scale'],
+ 'nullable' => !isset($props['nullable']) || !!$props['nullable'],
+ 'default' => ''.$props['default']
+ );
+ }
+ return array(
+ 'fd' => $table['fd'],
+ 'pk' => $table['pk'],
+ 'fk' => $table['fk'],
+ 'ix' => $this->normalize_index($table['ix'],$table['fd']),
+ 'uc' => $this->normalize_index($table['uc'],$table['fd'])
+ );
+ }
+
+ /*!
+ @function tables_identical
+ @syntax tables_identical( $old,$new )
+ @author ralfbecker
+ @abstract compares two table-definitions
+ @result True if they are identical or False else
+ */
+ function tables_identical($a,$b)
+ {
+ $a = serialize($this->normalize($a));
+ $b = serialize($this->normalize($b));
+
+ //echo "checking if tables identical = ".($a == $b ? 'True' : 'False')."
\n";
+ //echo "a: $a
\nb: $b
\n";
+
+ return $a == $b;
+ }
+ }
+
+
+